Skip to content

Instantly share code, notes, and snippets.

@mdecycu
Forked from mdecourse/1agrp_2022
Created March 16, 2022 18:42
Show Gist options
  • Save mdecycu/72fc7909f5934afb32bf717d0278e491 to your computer and use it in GitHub Desktop.
Save mdecycu/72fc7909f5934afb32bf717d0278e491 to your computer and use it in GitHub Desktop.
Brython examples
# 2a w3 grouping program, 與 2b 處理架構相同
from browser import html
from browser import document
import random
brython_div = document["brython_div"]
# 根據 href 與 content 將 html 元件中的 anchor 插入頁面
def makeLink(href, content):
brython_div <= html.A(content, href=href)
#brython_div <= html.BR()
# 1a
course_num = "0729"
# 2a
#course_num = "0752"
# 2b
#course_num = "0764"
reg_url = "https://nfulist.herokuapp.com/?semester=1102&courseno="+ course_num + "&column=True"
reg_data = open(reg_url).read().split("\n")[:-1]
#print(reg_data)
aorb = "a"
url = "https://mde.tw/studlist/2022spring/1a.txt"
course = "wcm2022"
# 從 url 讀取資料後, 以跳行符號分割資料進入數列後
# 去除數列中的第一筆與最後一筆資料後可得每位學員所填的資料
data = open(url).read().split("\n")[1:-1]
#print(data)
# 再以 \t 分割每位學員的資料,
#可以取得每位學員的學號, github 帳號與組別
big = []
num_github = {}
num_grp = {}
for i in data:
stud_no, github, grp_no = i.split("\t")
#print(stud_no)
#print(stud_no, github, grp_no)
# 因為納入新成員, 所以 big 必須之後才可組成
#big.append([stud_no, github, grp_no])
if github != "":
num_github[stud_no] = github
else:
num_github[stud_no] = stud_no
num_grp[stud_no] = grp_no
#print(num_grp)
# 根據最新註冊資料更新 studlist 中的內容
for i in reg_data:
# 納入新加選的學員或從 data 中移除已經退選者
# 假如最新修課學員學號並不在原名單中, 則屬加選者
if not(i in num_github):
#print(i)
# 先以學號作為帳號, 分組欄位空白
num_github[i] = i
num_grp[i] = ""
# 因為隨後查詢 num_github 與 num_grp 會以 reg_data 為主
# 在實作中可以無需從 num_github 或 num_grp 中移除退選者
for i in data:
# 表示該 i 學號已經退選
if not(i in reg_data):
# 將 i 學號分別從 num_gihub 與 num_grp 移除
try:
del num_github[i]
del num_grp[i]
except:
# 表示沒有退選者
pass
#print(num_github)
for i in reg_data:
big.append([i, num_github[i], num_grp[i]])
#print(big)
# 根據每一 element 的第三個 element sort
big.sort(key = lambda x: x[2])
# big 已經按照組別排序
#print(big)
ungrouped = []
grouped = []
for i in big:
if i[2] == "":
ungrouped.append(i[0])
else:
# 將組別放到第一位置
grouped.append([i[2], i[0]])
#print(grouped)
#print(ungrouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# 針對 2a.txt 處理第一時間大組人數超過 8 人者
# 將亂數留下 8 名成員, 其餘組員納入 ungrouped 數列
# grouped 重新回歸空數列
grouped = []
for i in group_member:
# 連同組序大於 9 表示組員總數大於 8
if len(i) > 9:
temp_member = i[1:]
# 以 shuffle 處理 temp_member
# 目的在隨機留下 8 位組員, 其餘納入 ungrouped
random.shuffle(temp_member)
# i[0] 為組序, temp_member[:8] 為前 8 位組員
grouped.append([i[0]] + temp_member[:8])
ungrouped = ungrouped + temp_member[8:]
else:
grouped.append(i)
#print(grouped)
#print(ungrouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# group_member 第一位為組序, 隨後為組員學號
#print(group_member)
random.shuffle(ungrouped)
#print("ungrouped:" + str(len(ungrouped)))
grp = 1
group = []
for i in group_member:
#print("grp " + str(i[0]) + ": num, " + str(len(i[1:])))
if len(i[1:]) < 6:
#print("can take " + str(8 - len(i[1:])) + "members")
# 若仍有學員未納組, 則可根據缺額補入學員學號
try:
#print("add " + str(ungrouped[:8-len(i[1:])]))
i.extend(list(ungrouped[:8-len(i[1:])]))
# 拿掉已經分配組別的學員學號
ungrouped = ungrouped[8-len(i[1:]):]
except:
#print("no member to add!")
pass
else:
#print("full")
pass
# 根據增量決定組序
i[0] = str(grp)
group.append(i)
grp += 1
# 假如各組已經全部補滿 8 人, 但 ungrouped 仍有學員
# 則依序從第一組依序補滿
ord = 0
#print(len(ungrouped))
if len(ungrouped) > 0:
for i in ungrouped:
group[ord].append(i)
ord += 1
#print(group)
# 根據最新的 group 資料更新 num_grp
# 先清空 num_grp
num_grp.clear()
for i in group:
# 組序為 element one
grp_order = i[0]
stud_list = i[1:]
for j in stud_list:
# j 為該組組員學號
num_grp[j] = grp_order
# 列出已經完成分組的結果, 準備更新至 mdecourse/studlist
newstud = []
print("1" + aorb + "\tgithub 帳號\t組別")
for i in reg_data:
#print(i)
# i 為學號
try:
print(i + "\t" + num_github[i] + "\t" + num_grp[i])
except:
newstud.append(i)
print("new: " + str(newstud))
for i in group:
brython_div <= "第" + str(i[0]) + "組:" + html.BR()
grp_repo = course + aorb + "g" + str(i[0])
for num in i[1:]:
# num 為各組組員學號
#print(num)
studhref = "https://"+ str(num_github[num]) + ".github.io/" + course
repohref = "https://github.com/"+ str(num_github[num]) +"/"+course
grphref = "https://"+ str(num_github[num]) + ".github.io/" + grp_repo
grp_repohref = "https://github.com/"+ str(num_github[num]) +"/" + grp_repo
brython_div <= "repo: "
makeLink(repohref, str(num))
brython_div <= " www: "
makeLink(studhref, str(num))
brython_div <= " " + grp_repo + "-repo: "
makeLink(grp_repohref, str(num))
brython_div <= " " + grp_repo + "-www: "
makeLink(grphref, str(num))
brython_div <= html.BR()
print("done")
# 程式自動猜數字遊戲
from browser import document, html, alert
import random
id4 = document["brython_div"]
執行次數 = 100
總猜測次數 = 0
for i in range(執行次數):
id4 <= "第" + str(i+1) + "次玩:" + html.BR()
下限 = 1
上限 = 100
標準答案 = random.randint(下限, 上限)
pc猜的數字 = random.randint(下限, 上限)
#print(標準答案, pc猜的數字)
#integer int()
#string str()
#float float()
#你猜的數字 = int(input("請輸入您所猜的整數:"))
猜測次數 = 1
while 標準答案 != pc猜的數字:
if 標準答案 < pc猜的數字:
#print("太大了,再猜一次 :)加油")
# 因此已經確定"pc猜的數字"不是答案, 因此 - 1
id4 <= "電腦猜的數字:" + str(pc猜的數字) + " 太大了!" + html.BR()
上限 = pc猜的數字 - 1
else:
#print("太小了,再猜一次 :)加油")
# 因此已經確定"pc猜的數字"不是答案, 因此 + 1
id4 <= "電腦猜的數字:" + str(pc猜的數字) + " 太小了!" + html.BR()
下限 = pc猜的數字 + 1
#pc猜的數字 = int(input("請輸入您所猜的整數:"))
pc猜的數字 = random.randint(下限, 上限)
猜測次數 += 1
#print("猜對了!總共猜了", 猜測次數, "次")
id4 <= "電腦猜對了, 答案為: " + str(標準答案) + ", 總共猜了 "+ str(猜測次數) + "次" + html.BR()
總猜測次數 += 猜測次數
平均猜測次數 = int(總猜測次數/執行次數)
#print("平均次數", 平均猜測次數)
id4 <= "平均次數: " + str(平均猜測次數)
# Ggame ball example
from ggame import (
App,
Color,
LineStyle,
Sprite,
RectangleAsset,
ImageAsset,
CircleAsset,
EllipseAsset,
PolygonAsset,
)
from browser import document as doc
from browser import html
# 引入既有的id="graphics-column" 標註
graphics_column = doc["graphics-column"]
# 建立內定名稱為 "ggame-canvas" 的 canvas 標註
canvas = html.CANVAS(width = 600, height = 100)
canvas.id = "ggame-canvas"
# 將 canvas 插入 gc 標註中
graphics_column <= canvas
# reverse - change the ball direction
def reverse(b):
b.direction *= -1
# Set up function for handling screen refresh
def step():
if ball.go:
ball.x += ball.direction
if ball.x + ball.width > myapp.width or ball.x < 0:
ball.x -= ball.direction
reverse(ball)
myapp = App()
# Three primary colors with no transparency (alpha = 1.0)
red = Color(0xff0000, 1.0)
green = Color(0x00ff00, 1.0)
blue = Color(0x0000ff, 1.0)
black = Color(0x000000, 1.0)
# define colors and line style
green = Color(0x00ff00, 1)
black = Color(0, 1)
noline = LineStyle(0, black)
# a rectangle asset and sprite to use as background
bg_asset = RectangleAsset(canvas.width, canvas.height, noline, green)
bg = Sprite(bg_asset, (0,0))
ball_asset = ImageAsset("/images/orb-150545_640.png")
ball = Sprite(ball_asset, (0, 0))
# Original image is too big. Scale it to 1/10 its original size
ball.scale = 0.1
# custom attributes
ball.direction = 7
ball.go = True
myapp.run(step)
# Cango 24v3 版本 Bezier 繪圖
from browser import window, html
from browser import document as doc
canvas = html.CANVAS(width = 600, height = 400)
canvas.id = "canvas"
brython_div = doc["brython_div"]
brython_div <= canvas
# Javascript 物件
cango = window.Cango.new
path = window.Path.new
shape = window.Shape.new
group = window.Group.new
# 24v3 circle 為 Javascript 物件
circle = window.circle.new
g1 = cango("canvas")
g1.clearCanvas()
g1.gridboxPadding(10, 10, 5, 7)
g1.fillGridbox("lightgreen")
g1.setWorldCoordsRHC(-100, -100, 400)
x1 = 40
y1 = 20
cx1 = 90
cy1 = 120
x2 = 120
y2 = 100
cx2 = 130
cy2 = 20
cx3 = 150
cy3 = 120
x3 = 180
y3 = 60
def dragC1(mousePos):
global cx1, cy1
cx1 = mousePos.x
cy1 = mousePos.y
drawCurve()
def dragC2(mousePos):
global cx2, cy2
cx2 = mousePos.x
cy2 = mousePos.y
drawCurve()
def dragC3(mousePos):
global cx3, cy3
cx3 = mousePos.x
cy3 = mousePos.y
drawCurve()
def drawCurve():
qbez = path(['M', x1, y1, 'Q', cx1, cy1, x2, y2], {
'strokeColor':'blue'})
cbez = path(['M', x2, y2, 'C', cx2, cy2, cx3, cy3, x3, y3], {
'strokeColor':'green'})
L1 = path(['M', x1, y1, 'L', cx1, cy1, x2, y2], {
'strokeColor':"rgba(0, 0, 0, 0.2)",
'dashed':[4]})
L2 = path(['M', x2, y2, 'L', cx2, cy2], {
'strokeColor':"rgba(0, 0, 0, 0.2)",
'dashed':[4]})
L3 = path(['M', x3, y3, 'L', cx3, cy3], {
'strokeColor':"rgba(0, 0, 0, 0.2)",
'dashed':[4]})
# 24v3 直接 translate 無需經過 tranform
c1.translate(cx1, cy1)
c2.translate(cx2, cy2)
c3.translate(cx3, cy3)
grp = group(qbez, cbez, L1, L2, L3, c1, c2, c3)
g1.render(grp, True)
g1.clearCanvas("lightyellow")
g1.deleteAllLayers()
g1.setWorldCoordsRHC(0, 0, 200)
c1 = shape(circle(6), {'fillColor':'red'})
c1.enableDrag(None, dragC1, None)
c2 = shape(circle(6), {'fillColor':'red'})
c2.enableDrag(None, dragC2, None)
c3 = shape(circle(6), {'fillColor':'red'})
c3.enableDrag(None, dragC3, None);
drawCurve()
# 大樂透電腦選號
# lottery
from browser import document, html, alert
import random
try:
total = int(input("請問要出幾張大樂透彩卷號碼?"))
except:
alert("請輸入要選擇大樂透電腦選號數量的'整數'")
total = int(input("請問要出幾張大樂透彩卷號碼?"))
# 準備將電腦選出的號碼, 輸出到內定 id="brython_div" 的標註區域
output_div = document["brython_div"]
output_div <= "以下將出 " + str(total) + " 張電腦選號彩卷:" + html.BR()
for i in range(1, total + 1):
# 利用 list(range()) 產生 1 到 49 的 population list
# 然後再透過 random.sample(population, k)
# 從 population, 產生 k 個不同的數字
numbers = random.sample(list(range(1, 49)), 6)
output_div <= str(i) + ". 電腦選號為: " + str(numbers) + html.BR()
# 導入 sys 模組
import sys
# 導入 keyword 模組
import keyword
# 利用 sys 模組中的 version_info 印出 Python 版次
print("Python version: ", sys.version_info)
# 利用 keyword 模組中的 kwlist 印出關鍵字
print("Python keywords: ", keyword.kwlist)
# from https://bmsleight.github.io/brython-blocks/ brython 3.3.5 to 3.9.0
# original source codes: https://github.com/bmsleight/brython-blocks/
# not complete
from browser import document, alert, html
from random import randint
# 用於定時執行特定函式
import browser.timer
# 利用 html 建立一個 CANVAS 標註物件, 與變數 grid 對應
grid = html.CANVAS(width = 0, height = 0)
next = html.CANVAS(width = 0, height = 0)
stop = html.BUTTON("Stop", id="stop")
detail = html.DIV(id="detail")
total = html.SPAN(id="total")
# 將 canvas 標註的 id 設為 "canvas"
grid.id = "grid"
next.id = "next"
# 將 document 中 id 為 "brython_div" 的標註
# 設為與 brython_div 變數對應
brython_div = document["brython_div"]
# 將 canvas 標註放入 brython_div 所在位置
# 頁面中原本就已經放入 <div id="brython_div"></div> 標註
brython_div <= grid + html.BR()
brython_div <= "Next Block" + html.BR() + next + html.BR()
brython_div <= detail + html.BR()
brython_div <= "Total Lines:" + total + html.BR()
brython_div <= stop +html.BR()
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應
#canvas = document["grid"]
# Globals
boarder_size = 5
w = 10 + boarder_size*2
h = 15 + boarder_size*2
block_size = 20
tick_timer = None
lines = 0
# Clash is when block + block >31
CLASH = 31
# Empty Blocks
WHITE = 0
GRAY = 1
BLOCK_HARD = 15
# Hard Blocks
BOARDER = 16
BOARDER_TOP = 17
RED = 18
BLUE = 19
ORANGE = 20
YELLOW = 21
MAGENTA = 22
CYAN = 23
GREEN = 24
def debug_to_browser(text):
debug = document["debug"]
debug.text = debug.text + str(text)
def update_lines_complete(removed):
global lines
lines = lines + removed
document["total"].text = lines
def paint_block(canvas_name, x, y, n):
if n == WHITE:
fill_style = "White"
elif n == GRAY:
fill_style = "Gray"
elif n == RED:
fill_style = "Red"
elif n == BLUE:
fill_style = "Blue"
elif n == ORANGE:
fill_style = "Orange"
elif n == YELLOW:
fill_style = "Yellow"
elif n == MAGENTA:
fill_style = "Magenta"
elif n == CYAN:
fill_style = "Cyan"
elif n == GREEN:
fill_style = "Green"
elif n == BOARDER:
fill_style = "Black"
elif n == BOARDER_TOP:
fill_style = "Pink"
else:
fill_style = "Pink"
if fill_style:
canvas=document[canvas_name].getContext("2d")
canvas.beginPath()
# Canvas 0,0 it top,left not bottom, left
# So we get the height and subtract our value
canvas.rect(x*block_size, document[canvas_name].height-y*block_size, block_size, block_size)
canvas.fillStyle = fill_style
canvas.fill()
class PlayingGrid():
def __init__(self):
# This looks the wrong way around but it create an x,y array
self.grid = [[GRAY for x in range(w)] for y in range(h)]
self.set_boarder()
def set_boarder(self):
for x in range(0, w):
for y in range(0, boarder_size):
self.grid[y][x] = BOARDER
for x in range(0, boarder_size):
for y in range(0, h):
self.grid[y][x] = BOARDER
for x in range(w-boarder_size, w):
for y in range(0, h):
self.grid[y][x] = BOARDER
def remove_complete_lines(self, by):
removed = 0
for row in self.grid[by:by+4]:
if not WHITE in row and \
not GRAY in row and \
not all(x == row[0] for x in row):
self.grid.remove(row)
removed = removed + 1
if removed:
for r in range(0, removed):
# Need a real new row - not the same one added
new_row = [GRAY for x in range(w)]
self.grid.append(new_row)
update_lines_complete(removed)
self.set_boarder()
return removed
def draw_grid(self):
for x in range(0,w):
for y in range(0,h):
paint_block("grid", x, y, self.grid[y][x])
class Block():
def __init__(self):
self.style = 0
self.total_styles = 7
self.rotation = 0
# Select a sytle by random, no rotation
self.style = randint(0,self.total_styles-1)
# Prep grid for rotating blocks
self.grid = [[0 for x in range(4)] for y in range(4)]
self.sytle_grid()
self.x = w/2 - 2
self.y = h - boarder_size
def sytle_grid(self):
# Rotation is counter-intuitive
# if rotate "true", its skips left to right
# So we precalculate the rotations so make them better
#I
if self.style == 0 and \
(self.rotation == 0 or self.rotation == 2):
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[ RED, RED, RED, RED],
[ 0, 0, 0, 0]]
if self.style == 0 and \
(self.rotation == 1 or self.rotation == 3):
self.grid = [[ 0, RED, 0, 0],
[ 0, RED, 0, 0],
[ 0, RED, 0, 0],
[ 0, RED, 0, 0]]
# O
if self.style == 1:
self.grid = [[ 0, 0, 0, 0],
[ 0, BLUE, BLUE, 0],
[ 0, BLUE, BLUE, 0],
[ 0, 0, 0, 0]]
#S
if self.style == 2 and \
(self.rotation == 0 or self.rotation == 2):
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, ORANGE, ORANGE],
[ 0, ORANGE, ORANGE, 0],
[ 0, 0, 0, 0]]
if self.style == 2 and \
(self.rotation == 1 or self.rotation == 3):
self.grid = [[ 0, 0, 0, 0],
[ 0, ORANGE, 0, 0],
[ 0, ORANGE, ORANGE, 0],
[ 0, 0, ORANGE, 0]]
#Z
if self.style == 3 and \
(self.rotation == 0 or self.rotation == 2):
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[ 0, YELLOW, YELLOW, 0],
[ 0, 0, YELLOW, YELLOW]]
if self.style == 3 and \
(self.rotation == 1 or self.rotation == 3):
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, YELLOW, 0],
[ 0, YELLOW, YELLOW, 0],
[ 0, YELLOW, 0, 0]]
if self.style == 4 and self.rotation == 0:
self.grid = [[ 0, 0, 0, 0],
[ 0, MAGENTA, 0, 0],
[MAGENTA, MAGENTA, MAGENTA, 0],
[ 0, 0, 0, 0]]
if self.style == 4 and self.rotation == 1:
self.grid = [[ 0, 0, 0, 0],
[ 0, MAGENTA, 0, 0],
[MAGENTA, MAGENTA, 0, 0],
[ 0, MAGENTA, 0, 0]]
if self.style == 4 and self.rotation == 2:
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[MAGENTA, MAGENTA, MAGENTA, 0],
[ 0, MAGENTA, 0, 0]]
if self.style == 4 and self.rotation == 3:
self.grid = [[ 0, 0, 0, 0],
[ 0, MAGENTA, 0, 0],
[ 0, MAGENTA, MAGENTA, 0],
[ 0, MAGENTA, 0, 0]]
# L
if self.style == 5 and self.rotation == 0:
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, CYAN, 0],
[CYAN, CYAN, CYAN, 0],
[ 0, 0, 0, 0]]
if self.style == 5 and self.rotation == 1:
self.grid = [[ 0, 0, 0, 0],
[CYAN, CYAN, 0, 0],
[ 0, CYAN, 0, 0],
[ 0, CYAN, 0, 0]]
if self.style == 5 and self.rotation == 2:
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[CYAN, CYAN, CYAN, 0],
[CYAN, 0, 0, 0]]
if self.style == 5 and self.rotation == 3:
self.grid = [[ 0, 0, 0, 0],
[ 0, CYAN, 0, 0],
[ 0, CYAN, 0, 0],
[ 0, CYAN, CYAN, 0]]
if self.style == 6 and self.rotation == 0:
self.grid = [[ 0, 0, 0, 0],
[GREEN, 0, 0, 0],
[GREEN, GREEN, GREEN, 0],
[ 0, 0, 0, 0]]
if self.style == 6 and self.rotation == 1:
self.grid = [[ 0, 0, 0, 0],
[ 0, GREEN, 0, 0],
[ 0, GREEN, 0, 0],
[GREEN, GREEN, 0, 0]]
if self.style == 6 and self.rotation == 2:
self.grid = [[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[GREEN, GREEN, GREEN, 0],
[ 0, 0, GREEN, 0]]
if self.style == 6 and self.rotation == 3:
self.grid = [[ 0, 0, 0, 0],
[ 0, GREEN, GREEN, 0],
[ 0, GREEN, 0, 0],
[ 0, GREEN, 0, 0]]
def rotate_anticlock(self):
self.rotation = self.rotation -1
if self.rotation == -1:
self.rotation = 3
self.sytle_grid()
def rotate_clock(self):
self.rotation = self.rotation +1
if self.rotation == 4:
self.rotation = 0
self.sytle_grid()
def paint(self, canvas_name):
for x in range(0,4):
for y in range(0,4):
paint_block(canvas_name, x, y, self.grid[y][x])
# Replace with function ?
def paint_block_on_grid():
print("paint_block_on_grid")
for x in range(-1,5):
for y in range(-1,5):
if ((x == -1 or x == 4) \
or (y == -1 or y == 4)) \
or (current_block.grid[y][x]<=BLOCK_HARD):
n = play_grid.grid[int(current_block.y + y)][int(current_block.x + x)]
else:
n = current_block.grid[y][x]
paint_block("grid", int(x+current_block.x), int(y+current_block.y), n)
def clash_blocks():
print("clash_blocks")
clash = False
for x in range(0,4):
for y in range(0,4):
b = current_block.grid[y][x] + play_grid.grid[int(y+current_block.y)][int(x+current_block.x)]
if b>CLASH:
clash = True
return clash
def freeze_current_block():
print("freeze_current_block")
for x in range(0,4):
for y in range(0,4):
if current_block.grid[y][x] > BLOCK_HARD:
play_grid.grid[int(y+current_block.y)][int(x+current_block.x)] = current_block.grid[y][x]
def test_new_position(movement):
if movement == "left":
current_block.x = current_block.x -1
elif movement == "right":
current_block.x = current_block.x +1
elif movement == "down":
current_block.y = current_block.y -1
elif movement == "rotate_c":
current_block.rotate_clock()
else:
pass
if clash_blocks():
if movement == "left":
current_block.x = current_block.x +1
return False
if movement == "right":
current_block.x = current_block.x -1
return False
if movement == "down":
current_block.y = current_block.y +1
return False
if movement == "rotate_c":
current_block.rotate_anticlock()
return False
else:
paint_block_on_grid()
return True
def key_code(ev):
# Debug
#trace = document["traceKeyCode"]
#trace.text = f'event {ev.type}, keyCode : {ev.keyCode}'
ev.stopPropagation()
# Key codes for Up, Down, Left, Right, wasd
if ev.keyCode == 37 or ev.keyCode == 65:
test_new_position("left")
if ev.keyCode == 39 or ev.keyCode == 68:
test_new_position("right")
if ev.keyCode == 40 or ev.keyCode == 83:
test_new_position("down")
if ev.keyCode == 38 or ev.keyCode == 87:
test_new_position("rotate_c")
def stop_timer(ev):
browser.timer.clear_interval(tick_timer)
def start_timer(ev):
tick_timer = browser.timer.set_interval(tick, 500)
def tick():
global current_block, next_block, play_grid
if not test_new_position("down"):
freeze_current_block()
play_grid.remove_complete_lines(current_block.y)
if not play_grid.remove_complete_lines(current_block.y) and \
not current_block.y < (h - boarder_size):
browser.timer.clear_interval(tick_timer)
alert("Game Over")
current_block = next_block
next_block = Block()
next_block.paint("next")
play_grid.draw_grid()
def init():
global tick_timer
element = document["grid"]
element.width = w*block_size
element.height = h*block_size
element = document["next"]
element.width = block_size*6
element.height = block_size*4
if not document["grid"].getContext:
alert('An error occured creating a Canvas 2D context. '
'This may be because you are using an old browser')
play_grid.draw_grid()
update_lines_complete(0)
next_block.paint("next")
tick_timer = browser.timer.set_interval(tick, 500)
document["stop"].bind('click',stop_timer)
document.onkeydown = key_code
play_grid = PlayingGrid()
play_grid.draw_grid()
current_block = Block()
next_block = Block()
init()
# Ggame
from ggame import App, ImageAsset, Sprite, MouseEvent
from random import random, randint
from browser import document as doc
from browser import html
import math
# 引入既有的id="graphics-column" 標註
graphics_column = doc["graphics-column"]
# 建立內定名稱為 "ggame-canvas" 的 canvas 標註
canvas = html.CANVAS(width = 600, height = 400)
canvas.id = "ggame-canvas"
# 將 canvas 插入 gc 標註中
graphics_column <= canvas
class Bunny(Sprite):
asset = ImageAsset("/images/bunny.png")
def __init__(self, position):
super().__init__(Bunny.asset, position)
# register mouse events
App.listenMouseEvent(MouseEvent.mousedown, self.mousedown)
App.listenMouseEvent(MouseEvent.mouseup, self.mouseup)
App.listenMouseEvent(MouseEvent.mousemove, self.mousemove)
self.dragging = True
self.deltax = 0
self.deltay = 0
def step(self):
# Every now and then a bunny hops...
if random() < 0.01:
self.x += randint(-20,20)
self.y += randint(-20,20)
def mousedown(self, event):
# capture any mouse down within 50 pixels
self.deltax = event.x - (self.x + self.width//2)
self.deltay = event.y - (self.y + self.height//2)
if abs(self.deltax) < 50 and abs(self.deltay) < 50:
self.dragging = True
# only drag one bunny at a time - consume the event
event.consumed = True
def mousemove(self, event):
if self.dragging:
self.x = event.x - self.deltax - self.width//2
self.y = event.y - self.deltay - self.height//2
event.consumed = True
def mouseup(self, event):
if self.dragging:
self.dragging = False
event.consumed = True
class DemoApp(App):
def __init__(self):
super().__init__()
for i in range(5):
Bunny((randint(50, 600), randint(50, 400)))
def step(self):
# Override step to perform action on each frame update
for bunny in self.spritelist:
bunny.step()
# Create the app
app = DemoApp()
# Run the app
app.run()
# Spur Gear in Cango and gearUtils-09.js
from browser import document as doc
from browser import html
from browser import window
import browser.timer
import math
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應
canvas = html.CANVAS(width = 600, height = 400)
# 將 canvas 標註的 id 設為 "cango_gear"
canvas.id = "cango_gear"
# 將 document 中 id 為 "brython_div" 的標註
# 設為與 brython_div 變數對應
brython_div = doc["brython_div"]
# 將 canvas 標註放入 brython_div 所在位置
brython_div <= canvas
# 將頁面中 id 為 cango_gear 的 CANVAS 設為與 canvas 對應
canvas = doc["cango_gear"]
# convert Javascript objects to Brython variables
cango = window.Cango.new
circle = window.circle.new
shape = window.Shape.new
path = window.Path.new
creategeartooth = window.createGearTooth.new
svgsegs = window.SVGsegs.new
# 經由 Cango 轉換成 Brython 的 cango
# 指定將圖畫在 id="cango_gear" 的 canvas 上
cgo = cango("cango_gear")
# 以下將要使用 gearUtils-09.js 畫出正齒輪外形
# 假設齒數為 25
num = 25
# 利用 gearUtils-09 產生單一齒輪外形資料
tooth = creategeartooth(10, num, 20)
# 在 Cango 中, 只有 SVG 才能 rotate, appendPath 或 joinPath
# 將齒輪外形轉為 SVG segment
toothSVG = svgsegs(tooth)
path1 = path(toothSVG.scale(1), {"degs": 45, "x": 100, "y": 100, "strokeColor": "#606060"})
#print(path1)
# SVG list
circle = circle(50)
#print(circle)
circleSVG = svgsegs(circle)
#print(circleSVG)
# 若將 circleSVG 轉為 Cango path, 則可以用 cgo.render()
#circlePath = path(circleSVG, {"x": 100, "y": 100, "strokeColor": "#606060"})
#cgo.render(circlePath)
# svgsegs 資料可以 joinPath 或 appendPath
# joinPath 按照頭尾順序銜接
# appendPath 則無順序銜接
# 從 toothSVG 複製出單齒 SVG 資料
one = toothSVG.dup()
# 以照齒數, 逐一複製並附加在原單齒資料中
# 第一齒的資料已經在 toothSVG 中, 因此重複迴圈從 1 開始
for i in range(1, num):
newSVG = one.rotate(360*i/num)
toothSVG = toothSVG.appendPath(newSVG)
# 將 SVG 轉為 path 資料
#gear = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"})
# path 資料可以透過 cgo.render()顯示繪圖物件
#cgo.render(gear)
# 當 circle 接外齒使用 appendPath
toothSVG = toothSVG.appendPath(circleSVG)
#print(toothSVG)
spurPath = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"})
cgo.render(spurPath)
# Spur Gear in Cango and gearUtils-09.js
from browser import document as doc
from browser import html
from browser import window
import browser.timer
import math
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應
canvas = html.CANVAS(width = 600, height = 400)
# 將 canvas 標註的 id 設為 "cango_gear"
canvas.id = "cango_gear"
# 將 document 中 id 為 "brython_div" 的標註
# 設為與 brython_div 變數對應
brython_div = doc["brython_div"]
# 將 canvas 標註放入 brython_div 所在位置
brython_div <= canvas
# 將頁面中 id 為 cango_gear 的 CANVAS 設為與 canvas 對應
canvas = doc["cango_gear"]
# convert Javascript objects to Brython variables
cango = window.Cango.new
circle = window.circle.new
shape = window.Shape.new
path = window.Path.new
creategeartooth = window.createGearTooth.new
svgsegs = window.SVGsegs.new
# 經由 Cango 轉換成 Brython 的 cango
# 指定將圖畫在 id="cango_gear" 的 canvas 上
cgo = cango("cango_gear")
# 以下將要使用 gearUtils-09.js 畫出正齒輪外形
# 假設齒數為 25
num = 25
# 利用 gearUtils-09 產生單一齒輪外形資料
tooth = creategeartooth(10, num, 20)
# 在 Cango 中, 只有 SVG 才能 rotate, appendPath 或 joinPath
# 將齒輪外形轉為 SVG segment
toothSVG = svgsegs(tooth)
path1 = path(toothSVG.scale(1), {"degs": 45, "x": 100, "y": 100, "strokeColor": "#606060"})
#print(path1)
# SVG list
circle = circle(50)
#print(circle)
circleSVG = svgsegs(circle)
#print(circleSVG)
# 若將 circleSVG 轉為 Cango path, 則可以用 cgo.render()
#circlePath = path(circleSVG, {"x": 100, "y": 100, "strokeColor": "#606060"})
#cgo.render(circlePath)
# svgsegs 資料可以 joinPath 或 appendPath
# joinPath 按照頭尾順序銜接
# appendPath 則無順序銜接
# 從 toothSVG 複製出單齒 SVG 資料
one = toothSVG.dup()
# 以照齒數, 逐一複製並附加在原單齒資料中
# 第一齒的資料已經在 toothSVG 中, 因此重複迴圈從 1 開始
for i in range(1, num):
newSVG = one.rotate(360*i/num)
toothSVG = toothSVG.appendPath(newSVG)
# 將 SVG 轉為 path 資料
#gear = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"})
# path 資料可以透過 cgo.render()顯示繪圖物件
#cgo.render(gear)
# 當 circle 接外齒使用 appendPath
toothSVG = toothSVG.appendPath(circleSVG)
#print(toothSVG)
spurPath = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"})
cgo.render(spurPath)
# Cango gearUtils-0.9.js Spur Gears
from browser import document as doc
from browser import html
from browser import window
import browser.timer
import math
canvas = html.CANVAS(width = 600, height = 400)
canvas.id = "cango_gear"
brython_div = doc["brython_div"]
brython_div <= canvas
canvas = doc["cango_gear"]
# 此程式採用 Cango Javascript 程式庫繪圖, 因此無需 ctx
#ctx = canvas.getContext("2d")
cango = window.Cango.new
path = window.Path.new
creategeartooth = window.createGearTooth.new
circle = window.circle.new
svgsegs = window.SVGsegs.new
# 經由 Cango 轉換成 Brython 的 cango, 指定將圖畫在 id="cango_gear" 的 canvas 上
cgo = cango("cango_gear")
######################################
# 畫正齒輪輪廓
#####################################
def cangoGear(n, m, pa, x=0, y=0, color="#606060"):
# n 為齒數
#n = 17
# pa 為壓力角
#pa = 25
# m 為模數, 根據畫布的寬度, 計算適合的模數大小
# Module = mm of pitch diameter per tooth
#m = 0.8*canvas.width/n
# pr 為節圓半徑
pr = n*m/2 # gear Pitch radius
# generate gear data
data = creategeartooth(m, n, pa)
toothSVG = svgsegs(data)
toothSVG.rotate(180/n) # rotate gear 1/2 tooth to mesh
# 單齒的齒形資料經過旋轉後, 將資料複製到 gear 物件中
one = toothSVG.dup()
# 利用單齒輪廓旋轉, 產生整個正齒輪外形
for i in range(1, n):
newSVG = one.rotate(360*i/n)
toothSVG = toothSVG.appendPath(newSVG)
# 建立軸孔
# add axle hole, hr 為 hole radius
hr = 0.6*pr # diameter of gear shaft
shaft = circle(hr)
shaftSVG = svgsegs(shaft)
spurSVG = toothSVG.appendPath(shaftSVG)
gear = path(spurSVG, {"x": x, "y": y, "strokeColor": color})
return gear
# 設定兩齒齒數
n1 = 84
n2 = 18
n3 = 99
# 使用 80% 的畫布寬度
m = 0.8*canvas.width/((n1+n2+n3))
# 設定共同的壓力角
pa = 25
# n 齒輪的節圓半徑
pr1 = n1*m/2
# n2 齒輪的節圓半徑
pr2 = n2*m/2
pr3 = n3*m/2
cx = canvas.width/2
cy = canvas.height/2
# Determine the coord of the middle gears
mcx = cx + (pr1-pr3)
mcy = cy
# 建立 gears
gear1 = cangoGear(n1, m, pa, color="red")
gear2 = cangoGear(n2, m, pa, color="green")
gear3 = cangoGear(n3, m, pa, color="blue")
deg = math.pi/180
rotate_speed = 0
def draw():
global rotate_speed
rotate_speed += 5*deg
cgo.clearCanvas()
theta1 = 0+rotate_speed
gear1.rotate(theta1)
gear1.translate(mcx-(pr1+pr2), mcy)
cgo.render(gear1)
theta2 = 180+(360/n2/2)-(rotate_speed)*n1/n2
gear2.rotate(theta2)
gear2.translate(mcx, mcy)
cgo.render(gear2)
theta3 = 180+(360/n3/2)+(180+(360/n2/2))*n2/n3+(rotate_speed*n1/n2)*(n2/n3)
gear3.rotate(theta3)
gear3.translate(mcx+(pr2+pr3), mcy)
cgo.render(gear3)
browser.timer.set_interval(draw, 2)
# 清除畫布
from browser import document, html
brython_div = document["brython_div"]
brython_div.clear()
# clock1 in Brython - 這是單行註解
# 以下則是多行註解
'''Code for the clock'''
# Line drawing
# 導入 doc
from browser import document as doc # 從模組 browser 導入 document 並且命名為 doc
from browser import html # 導入 html
import math # 導入數學模組
import time # 導入時間模組
import browser.timer # 導入用來製作 animation 動態模擬用的計時器
# 以下有兩種方式在頁面建立 id 為 line_drawing 的標註
# 假如網頁中已經存在 id 為 line_drawing 的 canvas, 則可以直接透過 document 指定 id 名稱取用
# 若網頁中沒有事先建立此一 canvas, 則可以透過 Brython 的 html 類別建立
canvas = html.CANVAS(width = 300, height = 200)
canvas.id = "line_drawing"
brython_div = doc["brython_div"]
brython_div <= canvas
# 準備繪圖畫布
canvas = doc["line_drawing"]
ctx = canvas.getContext("2d")
sin, cos = math.sin, math.cos
width, height = 200, 200 # canvas dimensions
ray = 100 # clock ray
background = "SteelBlue"
digits = "#fff"
border = "blue"
def needle(angle, r1, r2):
'''Draw a needle at specified angle in specified color.
r1 and r2 are percentages of clock ray.
'''
x1 = width / 2 - ray * cos(angle) * r1
y1 = height / 2 - ray * sin(angle) * r1
x2 = width / 2 + ray * cos(angle) * r2
y2 = height / 2 + ray * sin(angle) * r2
ctx.beginPath()
ctx.strokeStyle = "#fff"
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
ctx.stroke()
def set_clock():
# erase clock
ctx.beginPath()
ctx.fillStyle = background
ctx.arc(width / 2, height / 2, ray * 0.89, 0, 2 * math.pi)
ctx.fill()
# redraw hours
show_hours()
# print day
now_time = time.time()
now = time.localtime(now_time)
microsecs = now_time - int(now_time)
day = now.tm_mday
ctx.font = "bold 14px Arial"
ctx.textAlign = "center"
ctx.textBaseline = "middle"
ctx.fillStyle = "#000"
ctx.fillText(day, width * 0.7, height * 0.5)
# draw needles for hour, minute, seconds
ctx.lineWidth = 2
hour = now.tm_hour % 12 + now.tm_min / 60
angle = hour * 2 * math.pi / 12 - math.pi / 2
needle(angle, 0.05, 0.45)
minute = now.tm_min
angle = minute * 2 *math.pi / 60 - math.pi / 2
needle(angle, 0.05, 0.7)
ctx.lineWidth = 1
second = now.tm_sec + microsecs
angle = second * 2 * math.pi / 60 - math.pi / 2
needle(angle, 0.05, 0.8)
def show_hours():
ctx.beginPath()
ctx.arc(width / 2, height / 2, ray * 0.05, 0, 2 * math.pi)
ctx.fillStyle = digits
ctx.fill()
for i in range(1, 13):
angle = i * math.pi / 6 - math.pi / 2
x3 = width / 2 + ray * cos(angle) * 0.82
y3 = height / 2 + ray * sin(angle) * 0.82
ctx.font = "18px Arial"
ctx.textAlign = "center"
ctx.textBaseline = "middle"
ctx.fillText(i, x3, y3)
# cell for day
ctx.fillStyle = "#fff"
ctx.fillRect(width * 0.65, height * 0.47, width * 0.1, height * 0.06)
ctx.beginPath()
ctx.arc(width / 2, height / 2, ray, 0, 2 * math.pi)
ctx.fillStyle = background
ctx.fill()
# 每 100 micro seconds (0.1 second) 執行一次 set_clock 函式
browser.timer.set_interval(set_clock, 100)
show_hours()
# Temperature Conversion
# Temperature Conversion
'''
C to F: Celsius to Fahrenheit Conversion Formula. To convert temperatures in degrees Celsius to Fahrenheit, multiply by 1.8 (or 9/5) and add 32.
To convert temperatures in degrees Fahrenheit to Celsius, subtract 32 and multiply by .5556 (or 5/9).
Fahrenheit = Celsius*9/5 + 32
Celsium = (Fahrenheit - 32)*5/9
'''
from browser import document, html
brython_div = document["brython_div"]
def c2f(c):
f = round(c*9/5 + 32, 3)
return "Celsiusc: "+ str(c) + " degrees = Fahrenheit: " + str(f) + " degrees"
def f2c(f):
c = round((f - 32)*5/9, 3)
return "Fahrenheit: "+ str(f) + " degrees = Celsiusc: " + str(c) + " degrees"
choice = input("c2f or f2c")
if choice == "c2f":
'''
c = float(input("input Celsius in degrees"))
print(c2f(c))
'''
for deg in range(100):
brython_div <= c2f(deg)
brython_div <= html.BR()
else:
'''
f = float(input("input Fahrenheit in degrees"))
print(f2c(f))
'''
for deg in range(100):
brython_div <= f2c(deg)
brython_div <= html.BR()
# source: https://github.com/clarketm/mergedeep
from collections import Counter
from collections.abc import Mapping
from copy import deepcopy
from enum import Enum
from functools import reduce, partial
from typing import MutableMapping
class Strategy(Enum):
# Replace `destination` item with one from `source` (default).
REPLACE = 0
# Combine `list`, `tuple`, `set`, or `Counter` types into one collection.
ADDITIVE = 1
# Alias to: `TYPESAFE_REPLACE`
TYPESAFE = 2
# Raise `TypeError` when `destination` and `source` types differ. Otherwise, perform a `REPLACE` merge.
TYPESAFE_REPLACE = 3
# Raise `TypeError` when `destination` and `source` types differ. Otherwise, perform a `ADDITIVE` merge.
TYPESAFE_ADDITIVE = 4
def _handle_merge_replace(destination, source, key):
if isinstance(destination[key], Counter) and isinstance(source[key], Counter):
# Merge both destination and source `Counter` as if they were a standard dict.
_deepmerge(destination[key], source[key])
else:
# If a key exists in both objects and the values are `different`, the value from the `source` object will be used.
destination[key] = deepcopy(source[key])
def _handle_merge_additive(destination, source, key):
# Values are combined into one long collection.
if isinstance(destination[key], list) and isinstance(source[key], list):
# Extend destination if both destination and source are `list` type.
destination[key].extend(deepcopy(source[key]))
elif isinstance(destination[key], set) and isinstance(source[key], set):
# Update destination if both destination and source are `set` type.
destination[key].update(deepcopy(source[key]))
elif isinstance(destination[key], tuple) and isinstance(source[key], tuple):
# Update destination if both destination and source are `tuple` type.
destination[key] = destination[key] + deepcopy(source[key])
elif isinstance(destination[key], Counter) and isinstance(source[key], Counter):
# Update destination if both destination and source are `Counter` type.
destination[key].update(deepcopy(source[key]))
else:
_handle_merge[Strategy.REPLACE](destination, source, key)
def _handle_merge_typesafe(destination, source, key, strategy):
# Raise a TypeError if the destination and source types differ.
if type(destination[key]) is not type(source[key]):
raise TypeError(
f'destination type: {type(destination[key])} differs from source type: {type(source[key])} for key: "{key}"'
)
else:
_handle_merge[strategy](destination, source, key)
_handle_merge = {
Strategy.REPLACE: _handle_merge_replace,
Strategy.ADDITIVE: _handle_merge_additive,
Strategy.TYPESAFE: partial(_handle_merge_typesafe, strategy=Strategy.REPLACE),
Strategy.TYPESAFE_REPLACE: partial(_handle_merge_typesafe, strategy=Strategy.REPLACE),
Strategy.TYPESAFE_ADDITIVE: partial(_handle_merge_typesafe, strategy=Strategy.ADDITIVE),
}
def _is_recursive_merge(a, b):
both_mapping = isinstance(a, Mapping) and isinstance(b, Mapping)
both_counter = isinstance(a, Counter) and isinstance(b, Counter)
return both_mapping and not both_counter
def _deepmerge(dst, src, strategy=Strategy.REPLACE):
for key in src:
if key in dst:
if _is_recursive_merge(dst[key], src[key]):
# If the key for both `dst` and `src` are both Mapping types (e.g. dict), then recurse.
_deepmerge(dst[key], src[key], strategy)
elif dst[key] is src[key]:
# If a key exists in both objects and the values are `same`, the value from the `dst` object will be used.
pass
else:
_handle_merge.get(strategy)(dst, src, key)
else:
# If the key exists only in `src`, the value from the `src` object will be used.
dst[key] = deepcopy(src[key])
return dst
def merge(destination: MutableMapping, *sources: Mapping, strategy: Strategy = Strategy.REPLACE) -> MutableMapping:
"""
A deep merge function for 🐍.
:param destination: The destination mapping.
:param sources: The source mappings.
:param strategy: The merge strategy.
:return:
"""
return reduce(partial(_deepmerge, strategy=strategy), sources, destination)
# test the merge function
a = {"keyA": 1}
b = {"keyB": {"sub1": 10}}
c = {"keyB": {"sub2": 20}}
merged = merge({}, a, b, c)
print(merged)
# not complete yet
# STL viewer 原始檔案來自
# University of Wuppertal - http://mbi-wiki.uni-wuppertal.de/wordpress/
# Modified by Uli Eggersmann
# Binary STL 資料讀取原始作者 Oliver Marks - http://www.linux.com
# 原始檔案僅讀取 Text STL 零件檔案
# 2011 Fall 由 KMOL 新增 Binary STL 零件檔案讀取
from visual import scene, color, materials, faces, points
import os, struct
#file ="ritzel.stl"
file ="binary.stl"
scene.width = 400
scene.height = 400
scene.background = color.white # black
# 視窗標題取自 cvisual.pyd, 不可使用中文
scene.title = "STLViewer in VPython"
print ("利用滑鼠右鍵旋轉")
print ("滑鼠左右鍵同時按下後移動, 可以縮放畫面")
# Read STL file, only use vertex-line with xyz coordinates
list = []
#load stl file detects if the file is a text file or binary file
def load_stl(filename):
#read start of file to determine if its a binay stl file or a ascii stl file
fp=open(filename,'rb')
header=fp.read(80)
filetype=header[0:5]
# 這裡必須要能夠分辨二位元字串與文字字串
#print (type(filetype))
#print (filetype)
fp.close()
# for Python 3
if filetype==b'solid':
# for Python 2
#if filetype=='solid':
print ("讀取文字檔案格式:"+str(filename))
load_text_stl(filename)
else:
print ("讀取二位元檔案格式:"+str(filename,))
load_binary_stl(filename)
#load binary stl file check wikipedia for the binary layout of the file
#we use the struct library to read in and convert binary data into a format we can use
def load_binary_stl(filename):
'''
二位元 STL 檔案格式如下:
檔案標頭共有 80 個字元(bytes), 內容通常省略, 但是內容不可使用 solid, 以免與文字檔案 STL 混淆
UINT8[80] – Header
UINT32 – Number of triangles (I:佔 4 bytes 的 unsigned integer)
foreach triangle
REAL32[3] – Normal vector (f:每一座標分量為一佔 4 bytes 的 float, 共佔 12 bytes)
REAL32[3] – Vertex 1
REAL32[3] – Vertex 2
REAL32[3] – Vertex 3
UINT16 – Attribute byte count (H:兩個 bytes 的 unsigned short, 表示 attribute byte count)
end
'''
global list
fp=open(filename,'rb')
header=fp.read(80)
triangle_number = struct.unpack('I',fp.read(4))[0]
count=0
while True:
try:
p=fp.read(12)
if len(p)==12:
n=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
p=fp.read(12)
if len(p)==12:
p1=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
list.append(p1)
p=fp.read(12)
if len(p)==12:
p2=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
list.append(p2)
p=fp.read(12)
if len(p)==12:
p3=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
list.append(p3)
# 使用 count 來計算三角形平面個數
# triangle_number 為 STL 檔案中的三角形個數
count += 1
# 在前面 12*4 個 bytes 的 normal 與三個點資料後, 為
# 一個 2 bytes 長的 unsigned short, 其值為零, 為 attribute
fp.read(2)
# 讀完所有三角平面後, 即跳出 while
if count > triangle_number:
break
except EOFError:
break
fp.close()
def load_text_stl(filename):
global list
for dataline in open(filename,"r").readlines():
if not dataline.strip(): # skip blank lines
continue
field = dataline.split() # split with no argument makes the right place!
if field[0] == "vertex":
list.append([float(x) for x in field[1:4]])
#print (list)
#break
#for x in field[1:4]:
#print(x)
load_stl(os.path.abspath('')+'/'+file)
# Graphics
model = faces(pos=list, color=(0.8,0.8,0.8),
material=materials.plastic) # creates triangles
# 請注意, 這裡並沒有使用 STL 檔案中的平面 normal, 而是利用 VPython make_normals() 產生
model.make_normals() # creates plane normals
model.smooth(0.93) # smooths the edges
# = AllepunkteSTL points (pos = list, size = 3, color = Color.Black) # generates points
# Line drawing
# 導入 doc
from browser import document as doc
from browser import html
import math
canvas = html.CANVAS(width = 300, height = 200)
canvas.id = "line_drawing"
brython_div = doc["brython_div"]
brython_div <= canvas
# 準備繪圖畫布
canvas = doc["line_drawing"]
ctx = canvas.getContext("2d")
# 進行座標轉換, x 軸不變, y 軸反向且移動 canvas.height 單位光點
# ctx.setTransform(1, 0, 0, -1, 0, canvas.height)
# 以下採用 canvas 原始座標繪圖
# 設定填圖顏色
ctx.fillStyle = "rgb(200,0,0)"
# 設定畫筆顏色
ctx.strokeStyle = "rgb(0,0,200)"
# 乘上 deg 可轉為徑度單位
deg = math.pi / 180
# 建立多邊形定點位置畫線函式
def star(radius, xc, yc, n):
#radius = 100
#xc = 200
#yc = 200
xi = xc + radius*math.cos((360/n)*deg+90*deg)
yi = yc - radius*math.sin((360/n)*deg+90*deg)
ctx.beginPath()
ctx.moveTo(xi,yi)
for i in range(2, n+1):
x = xc + radius*math.cos((360/n)*deg*i+90*deg)
y = yc - radius*math.sin((360/n)*deg*i+90*deg)
ctx.lineTo(x,y)
# 以下利用多邊形畫線函式呼叫執行畫框線或填入顏色
# 畫五邊形框線
star(50, 50, 50, 5)
ctx.closePath()
ctx.stroke()
# 填三角形色塊
star(50, 150, 50, 3)
ctx.closePath()
ctx.fill()
# 改變畫線顏色後, 畫七邊形框線
ctx.strokeStyle = "rgb(0,200,20)"
star(50, 250, 50, 7)
ctx.closePath()
ctx.stroke()
# 導入 Ex1 原始碼
a = 0
def my_print(n, input_str):
print("以下將重覆列印 '", input_str + "' " + str(n) + "次")
for i in range(n):
print(i, input_str)
my_print(5, "Hello World!")
the_str = "開始學習 Python"
num = input("請輸入要列印的次數!")
my_print(int(num), the_str)
a = a + 1
print("(" + str(a) + ")", "_" * 25)
def myfun():
yield 1
yield 2
yield 3
for i in myfun():
print(i)
x = iter(myfun())
y = list(myfun())
a = a + 1
print("(" + str(a) + ")", "_" * 25)
print(x.__next__())
print(x.__next__())
print(x.__next__())
print(y)
# Python 的內建資料型別
import math
def f():
print("This is a user-defined function")
return 42
print("Some basic types in Python:")
print(type(2)) # int
print(type(2.2)) # float
print(type("2.2")) # str (string)
print(type(2 < 2.2)) # bool (boolean)
print(type(math)) # module
print(type(math.tan)) # builtin_function_or_method ("function" in Brython)
print(type(f)) # function (user-defined function)
print(type(type(42))) # type
print("#####################################################")
print("And some other types we will see later in the course...")
print(type(Exception())) # Exception
print(type(range(5))) # range
print(type([1,2,3])) # list
print(type((1,2,3))) # tuple
print(type({1,2})) # set
print(type({1:42})) # dict (dictionary or map)
print(type(2+3j)) # complex (complex number) (we may not see this type)
# Python 的內建常數與函式
# 內建常數
print("Some builtin constants:")
print(True)
print(False)
print(None)
print("And some more constants in the math module:")
import math
print(math.pi)
print(math.e)
# 內建函式
print("Type conversion functions:")
print(bool(0)) # convert to boolean (True or False)
print(float(42)) # convert to a floating point number
print(int(2.8)) # convert to an integer (int)
print("And some basic math functions:")
print(abs(-5)) # absolute value
print(max(2,3)) # return the max value
print(min(2,3)) # return the min value
print(pow(2,3)) # raise to the given power (pow(x,y) == x**y)
print(round(2.354, 1)) # round with the given number of digits
# other examples
print(3 * 2)
print(3 * "abc")
print(3 + 2)
print("abc" + "def")
print(3 + "def")
# Type Affect Semantics
print(3 * 2)
print(3 * "abc")
print(3 + 2)
print("abc" + "def")
print(3 + "def")
# Integer Division
print("The / operator does 'normal' float division:")
print(" 5/3 =", ( 5/3))
print()
print("The // operator does integer division:")
print(" 5//3 =", ( 5//3))
print(" 2//3 =", ( 2//3))
print("-1//3 =", (-1//3))
print("-4//3 =", (-4//3))
# Modulus or Remainder Operator (%)
print(" 6%3 =", ( 6%3))
print(" 5%3 =", ( 5%3))
print(" 2%3 =", ( 2%3))
print(" 0%3 =", ( 0%3))
print("-4%3 =", (-4%3))
print(" 3%0 =", ( 3%0))
# Verify that (a%b) is equivalent to (a-(a//b)*b)
def mod(a, b):
return a - (a//b)*b
print(41%14, mod(41,14))
print(14%41, mod(14,41))
print(-32%9, mod(-32,9))
print(32%-9, mod(32,-9))
# Operator Order (Precedence and Associativity)
print("Precedence:")
print(2+3*4) # prints 14, not 20
print(5+4%3) # prints 6, not 0 (% has same precedence as *, /, and //)
print(2**3*4) # prints 32, not 4096 (** has higher precedence than *, /, //, and %)
print()
print("Associativity:")
print(5-4-3) # prints -2, not 4 (- associates left-to-right)
print(4**3**2) # prints 262144, not 4096 (** associates right-to-left)
# Approximate Values of Floating-Point Numbers
print(0.1 + 0.1 == 0.2) # True, but...
print(0.1 + 0.1 + 0.1 == 0.3) # False!
print(0.1 + 0.1 + 0.1) # prints 0.30000000000000004 (uh oh)
print((0.1 + 0.1 + 0.1) - 0.3) # prints 5.55111512313e-17 (tiny, but non-zero!)
# Equality Testing with math.isclose
print("The problem....")
d1 = 0.1 + 0.1 + 0.1
d2 = 0.3
print(d1 == d2) # False (never use == with floats!)
print()
print("The solution...")
import math
print(math.isclose(d1, d2)) # True!
# math.isclose checks if the two numbers are ALMOST equal, within a small error
# Short-Circuit Evaluation
def yes():
return True
def no():
return False
def crash():
return 1/0 # crashes!
print(no() and crash()) # Works!
print(crash() and no()) # Crashes!
print (yes() and crash()) # Never runs (due to crash), but would also crash (without short-circuiting)
# Or operator
def yes():
return True
def no():
return False
def crash():
return 1/0 # crashes!
print(yes() or crash()) # Works!
print(crash() or yes()) # Crashes!
print(no() or crash()) # Never runs (due to crash), but would also crash (without short-circuiting)
# more examples
def isPositive(n):
result = (n > 0)
print("isPositive(",n,") =", result)
return result
def isEven(n):
result = (n % 2 == 0)
print("isEven(",n,") =", result)
return result
print("Test 1: isEven(-4) and isPositive(-4))")
print(isEven(-4) and isPositive(-4)) # Calls both functions
print("----------")
print("Test 2: isEven(-3) and isPositive(-3)")
print(isEven(-3) and isPositive(-3)) # Calls only one function!
# type vs isinstance
# Both type and isinstance can be used to type-check
# In general, (isinstance(x, T)) will be more robust than (type(x) == T)
print(type("abc") == str)
print(isinstance("abc", str))
# We'll see better reasons for this when we cover OOP + inheritance later
# in the course. For now, here is one reason: say you wanted to check
# if a value is any kind of number (int, float, complex, etc).
# You could do:
def isNumber(x):
return ((type(x) == int) or
(type(x) == float)) # are we sure this is ALL kinds of numbers?
print(isNumber(1), isNumber(1.1), isNumber(1+2j), isNumber("wow"))
# But this is cleaner, and works for all kinds of numbers, including
# complex numbers for example:
import numbers
def isNumber(x):
return isinstance(x, numbers.Number) # works for any kind of number
print(isNumber(1), isNumber(1.1), isNumber(1+2j), isNumber("wow"))
# Python 函式
# Vocabulary
x = 5
def f(y, z):
result = x + y + z
return result
print(f(1, 2)) # 8
print(f(3, 4)) # 12
# Vocabulary:
# variables: global vs. local
# statements vs. expressions
# function definitions vs. function calls
# parameters vs. arguments
# Return Statements
# Basic example
def isPositive(x):
return (x > 0)
print(isPositive(5)) # True
print(isPositive(-5)) # False
print(isPositive(0)) # False
# Return ends the function immediately
def isPositive(x):
print("Hello!") # runs
return (x > 0)
print("Goodbye!") # does not run ("dead code")
print(isPositive(5)) # prints Hello, then True
# No return statement --> return None
def f(x):
x + 42
print(f(5)) # None
def f2(x):
result = x + 42
print(f2(5)) # None
# Print versus Return
# This is a common early mistake (confusing print and return):
def cubed(x):
print(x**3) # Here is the error!
cubed(2) # seems to work!
print(cubed(3)) # sort of works (but prints None, which is weird)
print(2*cubed(4)) # Error!
# Once again (correctly)
def cubed2(x):
return (x**3) # That's better!
cubed2(2) # seems to be ignored (why?)
print(cubed2(3)) # works!
print(2*cubed2(4)) # works!
# Different Parameter and Return Types
def hypotenuse(a, b):
return ((a**2) + (b**2))**0.5
print(hypotenuse(3, 4)) # 5.0 (not 5)
print("---------------------")
def xor(b1, b2):
return ((b1 and (not b2)) or (b2 and (not b1))) # same as (b1 != b2)
print(xor(True, True)) # False
print(xor(True, False)) # True
print(xor(False, True)) # True
print(xor(False, False)) # False
print("---------------------")
def isPositive(n):
return (n > 0)
print(isPositive(10)) # True
print(isPositive(-1.234)) # False
# Function Composition
def f(w):
return 10*w
def g(x, y):
return f(3*x) + y
def h(z):
return f(g(z, f(z+1)))
print(h(1)) # hint: try the "visualize" feature
# Helper Functions
def onesDigit(n):
return n%10
def largerOnesDigit(x, y):
return max(onesDigit(x), onesDigit(y))
print(largerOnesDigit(134, 672)) # 4
print(largerOnesDigit(132, 674)) # Still 4
# Test Functions
# A broken test function
def onesDigit(n):
return n%10
def testOnesDigit():
print("Testing onesDigit()...", end="")
assert(onesDigit(5) == 5)
assert(onesDigit(123) == 3)
assert(onesDigit(100) == 0)
assert(onesDigit(999) == 9)
print("Passed!")
testOnesDigit() # Passed! Why is this bad?
# A better version
def onesDigit2(n):
return n%10
def testOnesDigit2():
print("Testing onesDigit()...", end="")
assert(onesDigit2(5) == 5)
assert(onesDigit2(123) == 3)
assert(onesDigit2(100) == 0)
assert(onesDigit2(999) == 9)
assert(onesDigit2(-123) == 3) # Added this test
print("Passed!")
testOnesDigit2() # Crashed! So the test function worked!
# Local Variable Scope
def f(x):
print("In f, x =", x)
x += 5
return x
def g(x):
return f(x*2) + f(x*3)
print(g(2))
# Another example
def f(x):
print("In f, x =", x)
x += 7
return round(x / 3)
def g(x):
x *= 10
return 2 * f(x)
def h(x):
x += 3
return f(x+4) + g(x)
print(h(f(1)))
# Global Variable Scope
# In general, you should avoid using global variables.
# You will even lose style points if you use them!
# Still, you need to understand how they work, since others
# will use them, and there may also be some very few occasions
# where you should use them, too!
g = 100
def f(x):
return x + g
print(f(5)) # 105
print(f(6)) # 106
print(g) # 100
# Another exampl
g = 100
def f(x):
# If we modify a global variable, we must declare it as global.
# Otherwise, Python will assume it is a local variable.
global g
g += 1
return x + g
print(f(5)) # 106
print(f(6)) # 108
print(g) # 102
# Default arguments
# Sometimes, a function has a parameter that has a natural default
# We can specify that default value in the function definition,
# then choose whether or not to include it in the function call.
def f(x, y=10):
return x + y
print(f(5)) # 15
print(f(5,1)) # 6
# import from downloads/py/fibo.py
import fibo
fibo.fib(5)
print(fibo.fib2(5))
# make canvas 600x400
from browser import document as doc
from browser import window
from browser import timer
from browser import html
import math
# 建立 fourbar canvas
canvas = html.CANVAS(width = 600, height = 400)
canvas.id = "fourbar1"
brython_div = doc["brython_div"]
brython_div <= canvas
# 準備繪圖畫布
canvas = doc["fourbar1"]
# 建立 buttons
brython_div <= html.BUTTON("啟動", id="power")
brython_div <= html.BUTTON("反向", id="reverse")
# 利用 window 擷取 PrairieDraw 程式庫變數物件, 然後以 JSConstructor 函式轉為 Brython 變數
pdraw = window.PrairieDraw.new
# 利用 window 擷取 PrairieDrawAnim 程式庫變數物件, 然後以 JSConstructor 函式轉為 Brython 變數
PrairieDrawAnim = window.PrairieDrawAnim.new
# 利用 window 擷取 sylvester 程式庫變數物件 Vector, 並將其 create 方法直接轉為 Brython 變數
# 在 sylvester 中的 $V 簡化變數無法直接在 Brython 程式中引用
vector = window.Vector.create.new
# 在 "fourbar" 畫布中建立 panim 動態模擬案例
panim = PrairieDrawAnim("fourbar1")
# 平面連桿繪圖以 t = 0 起始
t = 0
# 控制轉動方向變數
direction = True
# 繪製不同 t 時間下的平面連桿
def draw():
global t, direction, fast
# 設定模擬繪圖範圍
panim.setUnits(6, 6)
# 設定箭頭線寬
panim.setProp("arrowLineWidthPx",2)
# 起始變數設定
omega = 1
length_bar1 = 1
length_bar2 = 26/18
length_bar3 = 2
length_base = 40/18
time = 0
# 畫出地面直線
G = vector([0, -0.5])
panim.ground(G, vector([0, 1]), 10)
# 連桿長度與角度計算
A = t*omega # "theta"
AD = length_bar1 #length of left bar
AB = length_base #distance between two stationary pivots
BC = length_bar3 #length of right bar
CD = length_bar2 #length of middle bar
BD = math.sqrt(AD*AD + AB*AB - 2*AD*AB*math.cos(A))
C = math.acos((BC*BC + CD*CD - BD*BD)/(2*BC*CD))
ABD = math.asin(CD * math.sin(C) / BD)
DBC = math.asin(AD * math.sin(A) / BD)
B = ABD + DBC
D = math.pi - B - C
# draw pivot
pivot_left = vector([AB/-2, 0])
pivot_right = vector([AB/2, 0])
panim.pivot(vector([pivot_left.e(1), -0.5]), pivot_left, 0.5)
panim.pivot(vector([pivot_right.e(1), -0.5]), pivot_right, 0.5)
# 儲存轉換矩陣
panim.save()
#FIRST BAR
panim.translate(pivot_left)
panim.rotate(A)
panim.rod(vector([0,0]), vector([AD,0]), 0.25)
panim.point(vector([0,0]))
#SECOND BAR
panim.translate(vector([AD,0]))
panim.rotate(A*-1) #"undo" the original A rotation
panim.rotate(D) #rotate by D only
panim.rod(vector([0,0]), vector([CD,0]), 0.25)
panim.point(vector([0,0]))
#THIRD BAR
panim.translate(vector([CD,0]))
panim.rotate(math.pi+C)
panim.rod(vector([0,0]), vector([BC,0]), 0.25)
panim.point(vector([0,0]))
# 回復原先的轉換矩陣
panim.restore()
panim.point(vector([pivot_right.e(1), 0]))
# 時間增量
if direction == True:
t += 0.08
else:
t += -0.08
# 先畫出 t = 0 的連桿機構
draw()
# 將 anim 設為 None
anim = None
def launchAnimation(ev):
global anim
# 初始啟動, anim 為 None
if anim is None:
# 每 0.08 秒執行一次 draw 函式繪圖
anim = timer.set_interval(draw, 80)
# 初始啟動後, 按鈕文字轉為"暫停"
doc['power'].text = '暫停'
elif anim == 'hold':
# 當 anim 為 'hold' 表示曾經暫停後的啟動, 因此持續以 set_interval() 持續旋轉, 且將 power 文字轉為"暫停"
anim = timer.set_interval(draw, 80)
doc['power'].text = '暫停'
else:
# 初始啟動後, 使用者再按 power, 此時 anim 非 None 也不是 'hold', 因此會執行 clear_interval() 暫停
# 且將 anim 變數設為 'hold', 且 power 文字轉為"繼續"
timer.clear_interval(anim)
anim = 'hold'
doc['power'].text = '繼續'
def reverse(ev):
global anim, direction
# 當 anim 為 hold 時, 按鈕無效
if anim != "hold":
if direction == True:
direction = False
else:
direction = True
doc["power"].bind("click", launchAnimation)
doc["reverse"].bind("click", reverse)
# 2a w3 grouping program, 與 2b 處理架構相同
from browser import html
from browser import document
import random
brython_div = document["brython_div"]
# 根據 href 與 content 將 html 元件中的 anchor 插入頁面
def makeLink(href, content):
brython_div <= html.A(content, href=href)
#brython_div <= html.BR()
# 2a
course_num = "0752"
# 2b
#course_num = "0764"
reg_url = "https://nfulist.herokuapp.com/?semester=1102&courseno="+ course_num + "&column=True"
reg_data = open(reg_url).read().split("\n")[:-1]
#print(reg_data)
aorb = "a"
url = "https://mde.tw/studlist/2022spring/2a.txt"
course = "cd2022"
# 從 url 讀取資料後, 以跳行符號分割資料進入數列後
# 去除數列中的第一筆與最後一筆資料後可得每位學員所填的資料
data = open(url).read().split("\n")[1:-1]
#print(data)
# 再以 \t 分割每位學員的資料,
#可以取得每位學員的學號, github 帳號與組別
big = []
num_github = {}
num_grp = {}
for i in data:
stud_no, github, grp_no = i.split("\t")
#print(stud_no, github, grp_no)
# 因為納入新成員, 所以 big 必須之後才可組成
#big.append([stud_no, github, grp_no])
if github != "":
num_github[stud_no] = github
else:
num_github[stud_no] = stud_no
num_grp[stud_no] = grp_no
#print(num_grp)
# 根據最新註冊資料更新 studlist 中的內容
for i in reg_data:
# 納入新加選的學員或從 data 中移除已經退選者
# 假如最新修課學員學號並不在原名單中, 則屬加選者
if not(i in num_github):
#print(i)
# 先以學號作為帳號, 分組欄位空白
num_github[i] = i
num_grp[i] = ""
# 因為隨後查詢 num_github 與 num_grp 會以 reg_data 為主
# 在實作中可以無需從 num_github 或 num_grp 中移除退選者
for i in data:
# 表示該 i 學號已經退選
if not(i in reg_data):
# 將 i 學號分別從 num_gihub 與 num_grp 移除
try:
del num_github[i]
del num_grp[i]
except:
# 表示沒有退選者
pass
#print(num_github)
for i in reg_data:
big.append([i, num_github[i], num_grp[i]])
#print(big)
# 根據每一 element 的第三個 element sort
big.sort(key = lambda x: x[2])
# big 已經按照組別排序
#print(big)
ungrouped = []
grouped = []
for i in big:
if i[2] == "":
ungrouped.append(i[0])
else:
# 將組別放到第一位置
grouped.append([i[2], i[0]])
#print(grouped)
#print(ungrouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# 針對 2a.txt 處理第一時間大組人數超過 8 人者
# 將亂數留下 8 名成員, 其餘組員納入 ungrouped 數列
# grouped 重新回歸空數列
grouped = []
for i in group_member:
# 連同組序大於 9 表示組員總數大於 8
if len(i) > 9:
temp_member = i[1:]
# 以 shuffle 處理 temp_member
# 目的在隨機留下 8 位組員, 其餘納入 ungrouped
random.shuffle(temp_member)
# i[0] 為組序, temp_member[:8] 為前 8 位組員
grouped.append([i[0]] + temp_member[:8])
ungrouped = ungrouped + temp_member[8:]
else:
grouped.append(i)
#print(grouped)
#print(ungrouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# group_member 第一位為組序, 隨後為組員學號
#print(group_member)
random.shuffle(ungrouped)
#print("ungrouped:" + str(len(ungrouped)))
grp = 1
group = []
for i in group_member:
#print("grp " + str(i[0]) + ": num, " + str(len(i[1:])))
if len(i[1:]) < 8:
#print("can take " + str(8 - len(i[1:])) + "members")
# 若仍有學員未納組, 則可根據缺額補入學員學號
try:
#print("add " + str(ungrouped[:8-len(i[1:])]))
i.extend(list(ungrouped[:8-len(i[1:])]))
# 拿掉已經分配組別的學員學號
ungrouped = ungrouped[8-len(i[1:]):]
except:
#print("no member to add!")
pass
else:
#print("full")
pass
# 根據增量決定組序
i[0] = str(grp)
group.append(i)
grp += 1
# 假如各組已經全部補滿 8 人, 但 ungrouped 仍有學員
# 則依序從第一組依序補滿
ord = 0
#print(len(ungrouped))
if len(ungrouped) > 0:
for i in ungrouped:
group[ord].append(i)
ord += 1
#print(group)
# 根據最新的 group 資料更新 num_grp
# 先清空 num_grp
num_grp.clear()
for i in group:
# 組序為 element one
grp_order = i[0]
stud_list = i[1:]
for j in stud_list:
# j 為該組組員學號
num_grp[j] = grp_order
# 列出已經完成分組的結果, 準備更新至 mdecourse/studlist
newstud = []
print("2" + aorb + "\tgithub 帳號\t組別")
for i in reg_data:
#print(i)
# i 為學號
try:
print(i + "\t" + num_github[i] + "\t" + num_grp[i])
except:
newstud.append(i)
print("new: " + str(newstud))
for i in group:
brython_div <= "第" + str(i[0]) + "組:" + html.BR()
grp_repo = course + aorb + "g" + str(i[0])
for num in i[1:]:
# num 為各組組員學號
#print(num)
studhref = "https://"+ str(num_github[num]) + ".github.io/" + course
repohref = "https://github.com/"+ str(num_github[num]) +"/"+course
grphref = "https://"+ str(num_github[num]) + ".github.io/" + grp_repo
grp_repohref = "https://github.com/"+ str(num_github[num]) +"/" + grp_repo
brython_div <= "repo: "
makeLink(repohref, str(num))
brython_div <= " www: "
makeLink(studhref, str(num))
brython_div <= " " + grp_repo + "-repo: "
makeLink(grp_repohref, str(num))
brython_div <= " " + grp_repo + "-www: "
makeLink(grphref, str(num))
brython_div <= html.BR()
print("done")
# 2b w3 grouping program, 與 2a 處理架構相同
from browser import html
from browser import document
import random
brython_div = document["brython_div"]
# 根據 href 與 content 將 html 元件中的 anchor 插入頁面
def makeLink(href, content):
brython_div <= html.A(content, href=href)
#brython_div <= html.BR()
# 2a
#course_num = "0752"
# 2b
course_num = "0764"
reg_url = "https://nfulist.herokuapp.com/?semester=1102&courseno="+ course_num + "&column=True"
reg_data = open(reg_url).read().split("\n")[:-1]
#print(reg_data)
aorb = "b"
url = "https://mde.tw/studlist/2022spring/2b.txt"
course = "cd2022"
# 從 url 讀取資料後, 以跳行符號分割資料進入數列後
# 去除數列中的第一筆與最後一筆資料後可得每位學員所填的資料
data = open(url).read().split("\n")[1:-1]
#print(data)
# 再以 \t 分割每位學員的資料,
#可以取得每位學員的學號, github 帳號與組別
big = []
num_github = {}
num_grp = {}
for i in data:
stud_no, github, grp_no = i.split("\t")
#print(stud_no, github, grp_no)
# 因為納入新成員, 所以 big 必須之後才可組成
#big.append([stud_no, github, grp_no])
if github != "":
num_github[stud_no] = github
else:
num_github[stud_no] = stud_no
num_grp[stud_no] = grp_no
#print(num_grp)
# 根據最新註冊資料更新 studlist 中的內容
for i in reg_data:
# 納入新加選的學員或從 data 中移除已經退選者
# 假如最新修課學員學號並不在原名單中, 則屬加選者
if not(i in num_github):
#print(i)
# 先以學號作為帳號, 分組欄位空白
num_github[i] = i
num_grp[i] = ""
# 因為隨後查詢 num_github 與 num_grp 會以 reg_data 為主
# 在實作中可以無需從 num_github 或 num_grp 中移除退選者
for i in data:
# 表示該 i 學號已經退選
if not(i in reg_data):
# 將 i 學號分別從 num_gihub 與 num_grp 移除
try:
del num_github[i]
del num_grp[i]
except:
# 表示沒有退選者
pass
#print(num_github)
for i in reg_data:
big.append([i, num_github[i], num_grp[i]])
#print(big)
# 根據每一 element 的第三個 element sort
big.sort(key = lambda x: x[2])
# big 已經按照組別排序
#print(big)
ungrouped = []
grouped = []
for i in big:
if i[2] == "":
ungrouped.append(i[0])
else:
# 將組別放到第一位置
grouped.append([i[2], i[0]])
#print(grouped)
#print(ungrouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# 針對 2a.txt 處理第一時間大組人數超過 8 人者
# 將亂數留下 8 名成員, 其餘組員納入 ungrouped 數列
# grouped 重新回歸空數列
grouped = []
for i in group_member:
# 連同組序大於 9 表示組員總數大於 8
if len(i) > 9:
temp_member = i[1:]
# 以 shuffle 處理 temp_member
# 目的在隨機留下 8 位組員, 其餘納入 ungrouped
random.shuffle(temp_member)
# i[0] 為組序, temp_member[:8] 為前 8 位組員
grouped.append([i[0]] + temp_member[:8])
ungrouped = ungrouped + temp_member[8:]
else:
grouped.append(i)
#print(grouped)
#print(ungrouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# group_member 第一位為組序, 隨後為組員學號
#print(group_member)
random.shuffle(ungrouped)
#print("ungrouped:" + str(len(ungrouped)))
grp = 1
group = []
for i in group_member:
#print("grp " + str(i[0]) + ": num, " + str(len(i[1:])))
if len(i[1:]) < 8:
#print("can take " + str(8 - len(i[1:])) + "members")
# 若仍有學員未納組, 則可根據缺額補入學員學號
try:
#print("add " + str(ungrouped[:8-len(i[1:])]))
i.extend(list(ungrouped[:8-len(i[1:])]))
# 拿掉已經分配組別的學員學號
ungrouped = ungrouped[8-len(i[1:]):]
except:
#print("no member to add!")
pass
else:
#print("full")
pass
# 根據增量決定組序
i[0] = str(grp)
group.append(i)
grp += 1
# 假如各組已經全部補滿 8 人, 但 ungrouped 仍有學員
# 則依序從第一組依序補滿
ord = 0
#print(len(ungrouped))
if len(ungrouped) > 0:
for i in ungrouped:
group[ord].append(i)
ord += 1
#print(group)
# 根據最新的 group 資料更新 num_grp
# 先清空 num_grp
num_grp.clear()
for i in group:
# 組序為 element one
grp_order = i[0]
stud_list = i[1:]
for j in stud_list:
# j 為該組組員學號
num_grp[j] = grp_order
# 列出已經完成分組的結果, 準備更新至 mdecourse/studlist
newstud = []
print("2" + aorb + "\tgithub 帳號\t組別")
for i in reg_data:
#print(i)
# i 為學號
try:
print(i + "\t" + num_github[i] + "\t" + num_grp[i])
except:
newstud.append(i)
print("new: " + str(newstud))
for i in group:
brython_div <= "第" + str(i[0]) + "組:" + html.BR()
grp_repo = course + aorb + "g" + str(i[0])
for num in i[1:]:
# num 為各組組員學號
#print(num)
studhref = "https://"+ str(num_github[num]) + ".github.io/" + course
repohref = "https://github.com/"+ str(num_github[num]) +"/"+course
grphref = "https://"+ str(num_github[num]) + ".github.io/" + grp_repo
grp_repohref = "https://github.com/"+ str(num_github[num]) +"/" + grp_repo
brython_div <= "repo: "
makeLink(repohref, str(num))
brython_div <= " www: "
makeLink(studhref, str(num))
brython_div <= " " + grp_repo + "-repo: "
makeLink(grp_repohref, str(num))
brython_div <= " " + grp_repo + "-www: "
makeLink(grphref, str(num))
brython_div <= html.BR()
print("done")
# 猜數字遊戲
from browser import document, html, alert
import random
# 跳出文字說明視窗
alert("開始玩猜數字遊戲")
# 利用 random 模組中的 randint 取 1~100 間的亂數
標準答案 = random.randint(1, 100)
# 利用 input 函式視窗, 取使用者所猜的數字, 轉為整數
你猜的數字 = int(input("請輸入您所猜 1~100 間的整數:"))
# 猜測次數起始值設為 1
猜測次數 = 1
# 進入重複迴圈, 直到猜對數字
while 標準答案 != 你猜的數字:
# 根據使用者所猜的數字, 與答案比較後, 給出提示
if 標準答案 < 你猜的數字:
alert("猜第" + str(猜測次數) + "次, 太大了,再猜 :)加油")
else:
alert("猜第" + str(猜測次數) + "次, 太小了,再猜 :)加油")
你猜的數字 = int(input("請輸入您所猜 1~100 間的整數:"))
# 猜測次數累加
猜測次數 += 1
# 跳出迴圈表示猜對, 給出最後文字說明視窗
alert("猜對了!答案為" + str(標準答案) + ", 總共猜了" + str(猜測次數) + "次")
# 從 browser 導入 html
from browser import html
# 從 browser 導入 document 並且對應為 doc
from browser import document as doc
# 導入 browser.timer
import browser.timer
# 定義一個 game() 函式
def game():
"""
利用 global 關鍵字 將 px, py 與 speed
設為可在函式內改變對應內容
(意即, 這三個定義在函式外的全域變數,
在函式中分別位於等號左邊)
"""
global px, py, speed
ctx.clearRect(px, py, width, height)
ctx.fillStyle = "red"
if px < canvas.width/2:
px += speed
else:
py -= speed
if px < 0 or (px + width) > canvas.width:
speed = -speed
if py < 0 or (py + height) > canvas.height:
speed = -speed
ctx.fillRect(px, py, width, height)
"""
a variable declared outside of the function or
in global scope is known as a global variable.
This means that a global variable can be accessed
inside or outside of the function.
"""
canvas = html.CANVAS(width = 600, height = 600)
canvas.id = "game-board"
brython_div = doc["brython_div"]
brython_div <= canvas
ctx = canvas.getContext("2d")
px = 0
py = 50
width = 20
height = 20
speed = 2
browser.timer.set_interval(game, 10)
from browser import document as doc
from browser import timer
from browser import html
import math
# 建立 game-board canvas
canvas = html.CANVAS(width = 300, height = 300)
canvas.id = "game-board"
brython_div = doc["brython_div"]
brython_div <= canvas
ctx = canvas.getContext("2d")
px = 0
py = 50
width = 20
height = 20
speed = 2
# 建立 button
brython_div <= html.BUTTON("啟動", id="power")
def game():
global px, py, speed
ctx.clearRect(px, py, width, height)
ctx.fillStyle = "red"
if px < canvas.width/2:
px += speed
else:
py -= speed
if px < 0 or (px + width) > canvas.width:
speed = -speed
if py < 0 or (py + height) > canvas.height:
speed = -speed
ctx.fillRect(px, py, width, height)
# 將 anim 設為 None
anim = None
def launchAnimation(ev):
global anim
# 初始啟動, anim 為 None
if anim is None:
# 每 0.1 秒執行一次 draw 函式繪圖
anim = timer.set_interval(game, 10)
# 初始啟動後, 按鈕文字轉為"暫停"
doc['power'].text = '暫停'
elif anim == 'hold':
# 當 anim 為 'hold' 表示曾經暫停後的啟動, 因此持續以 set_interval() 持續旋轉, 且將 power 文字轉為"暫停"
anim = timer.set_interval(game, 10)
doc['power'].text = '暫停'
else:
# 初始啟動後, 使用者再按 power, 此時 anim 非 None 也不是 'hold', 因此會執行 clear_interval() 暫停
# 且將 anim 變數設為 'hold', 且 power 文字轉為"繼續"
timer.clear_interval(anim)
anim = 'hold'
doc['power'].text = '繼續'
doc["power"].bind("click", launchAnimation)
from browser import document as doc
from browser import html
# get brython_div
brython_div = doc["brython_div"]
# 插入輸入表單
form = html.FORM()
input = html.INPUT(type="text", id="input")
output = html.DIV(id="output")
form <= "Keydown: " + input + html.BR()
brython_div <= form + html.BR()
brython_div <= output + html.BR()
def keyCode(ev):
trace = doc["output"]
trace.text = f"event: {ev.type}, code: {ev.code}"
print(ev.keyCode)
ev.stopPropagation()
doc["input"].bind("keydown", keyCode)
#document["codeKeypress"].bind("keypress", keyCode)
#document["codeKeyup"].bind("keyup", keyCode)
# Konva1 繪圖
# 引用 https://konvajs.github.io/ 繪圖
from browser import document, html, window
width = 600
height = 400
konva = window.Konva
# Konva 必須在 canvas 繪圖, 從上方設定, canvas id 為 "container"
stage = konva.Stage.new({
"container": 'brython_div',
"width": width,
"height": height
})
layer = konva.Layer.new()
rectX = stage.getWidth() / 2 - 50
rectY = stage.getHeight() / 2 - 25
box = konva.Rect.new({
"x": rectX,
"y": rectY,
"width": 100,
"height": 50,
"fill": '#00D2FF',
"stroke": 'black',
"strokeWidth": 4,
"draggable": True
})
def f1():
document.body.style.cursor = 'pointer'
def f2():
document.body.style.cursor = 'default'
# add cursor styling
box.on('mouseover', f1())
box.on('mouseout', f2())
layer.add(box)
stage.add(layer)
# source: https://github.com/breezy-team/merge3
class CantReprocessAndShowBase(Exception):
"""Can't reprocess and show base."""
def intersect(ra, rb):
"""Given two ranges return the range where they intersect or None.
>>> intersect((0, 10), (0, 6))
(0, 6)
>>> intersect((0, 10), (5, 15))
(5, 10)
>>> intersect((0, 10), (10, 15))
>>> intersect((0, 9), (10, 15))
>>> intersect((0, 9), (7, 15))
(7, 9)
"""
# preconditions: (ra[0] <= ra[1]) and (rb[0] <= rb[1])
sa = max(ra[0], rb[0])
sb = min(ra[1], rb[1])
if sa < sb:
return sa, sb
else:
return None
def compare_range(a, astart, aend, b, bstart, bend):
"""Compare a[astart:aend] == b[bstart:bend], without slicing.
"""
if (aend-astart) != (bend-bstart):
return False
for ia, ib in zip(range(astart, aend), range(bstart, bend)):
if a[ia] != b[ib]:
return False
else:
return True
class Merge3(object):
"""3-way merge of texts.
Given BASE, OTHER, THIS, tries to produce a combined text
incorporating the changes from both BASE->OTHER and BASE->THIS.
All three will typically be sequences of lines."""
def __init__(self, base, a, b, is_cherrypick=False, sequence_matcher=None):
"""Constructor.
:param base: lines in BASE
:param a: lines in A
:param b: lines in B
:param is_cherrypick: flag indicating if this merge is a cherrypick.
When cherrypicking b => a, matches with b and base do not conflict.
:param sequence_matcher: Sequence matcher to use (defaults to
difflib.SequenceMatcher)
"""
if sequence_matcher is None:
import difflib
sequence_matcher = difflib.SequenceMatcher
self.base = base
self.a = a
self.b = b
self.is_cherrypick = is_cherrypick
self.sequence_matcher = sequence_matcher
def _uses_bytes(self):
if len(self.a) > 0:
return isinstance(self.a[0], bytes)
elif len(self.base) > 0:
return isinstance(self.base[0], bytes)
elif len(self.b) > 0:
return isinstance(self.b[0], bytes)
else:
return False
def merge_lines(self,
name_a=None,
name_b=None,
name_base=None,
start_marker='<<<<<<<',
mid_marker='=======',
end_marker='>>>>>>>',
base_marker=None,
reprocess=False):
"""Return merge in cvs-like form.
"""
if base_marker and reprocess:
raise CantReprocessAndShowBase()
if self._uses_bytes():
if len(self.a) > 0:
if self.a[0].endswith(b'\r\n'):
newline = b'\r\n'
elif self.a[0].endswith(b'\r'):
newline = b'\r'
else:
newline = b'\n'
else:
newline = b'\n'
if isinstance(start_marker, str):
start_marker = start_marker.encode()
if isinstance(end_marker, str):
end_marker = end_marker.encode()
if isinstance(mid_marker, str):
mid_marker = mid_marker.encode()
if base_marker is not None and isinstance(base_marker, str):
base_marker = base_marker.encode()
if name_a:
if isinstance(name_a, str):
name_a = name_a.encode()
start_marker = start_marker + b' ' + name_a
if name_b:
if isinstance(name_b, str):
name_b = name_b.encode()
end_marker = end_marker + b' ' + name_b
if name_base and base_marker:
if isinstance(name_base, str):
name_base = name_base.encode()
base_marker = base_marker + b' ' + name_base
else:
if len(self.a) > 0:
if self.a[0].endswith('\r\n'):
newline = '\r\n'
elif self.a[0].endswith('\r'):
newline = '\r'
else:
newline = '\n'
else:
newline = '\n'
if name_a:
start_marker = start_marker + ' ' + name_a
if name_b:
end_marker = end_marker + ' ' + name_b
if name_base and base_marker:
base_marker = base_marker + ' ' + name_base
merge_regions = self.merge_regions()
if reprocess is True:
merge_regions = self.reprocess_merge_regions(merge_regions)
for t in merge_regions:
what = t[0]
if what == 'unchanged':
for i in range(t[1], t[2]):
yield self.base[i]
elif what == 'a' or what == 'same':
for i in range(t[1], t[2]):
yield self.a[i]
elif what == 'b':
for i in range(t[1], t[2]):
yield self.b[i]
elif what == 'conflict':
yield start_marker + newline
for i in range(t[3], t[4]):
yield self.a[i]
if base_marker is not None:
yield base_marker + newline
for i in range(t[1], t[2]):
yield self.base[i]
yield mid_marker + newline
for i in range(t[5], t[6]):
yield self.b[i]
yield end_marker + newline
else:
raise ValueError(what)
def merge_annotated(self):
"""Return merge with conflicts, showing origin of lines.
Most useful for debugging merge.
"""
UNCHANGED = 'u'
SEP = ' | '
CONFLICT_START = '<<<<\n'
CONFLICT_MID = '----\n'
CONFLICT_END = '>>>>\n'
WIN_A = 'a'
WIN_B = 'b'
if self._uses_bytes():
UNCHANGED = UNCHANGED.encode()
SEP = SEP.encode()
CONFLICT_START = CONFLICT_START.encode()
CONFLICT_MID = CONFLICT_MID.encode()
CONFLICT_END = CONFLICT_END.encode()
WIN_A = WIN_A.encode()
WIN_B = WIN_B.encode()
for t in self.merge_regions():
what = t[0]
if what == 'unchanged':
for i in range(t[1], t[2]):
yield UNCHANGED + SEP + self.base[i]
elif what == 'a' or what == 'same':
for i in range(t[1], t[2]):
yield WIN_A.lower() + SEP + self.a[i]
elif what == 'b':
for i in range(t[1], t[2]):
yield WIN_B.lower() + SEP + self.b[i]
elif what == 'conflict':
yield CONFLICT_START
for i in range(t[3], t[4]):
yield WIN_A.upper() + SEP + self.a[i]
yield CONFLICT_MID
for i in range(t[5], t[6]):
yield WIN_B.upper() + SEP + self.b[i]
yield CONFLICT_END
else:
raise ValueError(what)
def merge_groups(self):
"""Yield sequence of line groups. Each one is a tuple:
'unchanged', lines
Lines unchanged from base
'a', lines
Lines taken from a
'same', lines
Lines taken from a (and equal to b)
'b', lines
Lines taken from b
'conflict', base_lines, a_lines, b_lines
Lines from base were changed to either a or b and conflict.
"""
for t in self.merge_regions():
what = t[0]
if what == 'unchanged':
yield what, self.base[t[1]:t[2]]
elif what == 'a' or what == 'same':
yield what, self.a[t[1]:t[2]]
elif what == 'b':
yield what, self.b[t[1]:t[2]]
elif what == 'conflict':
yield (what,
self.base[t[1]:t[2]],
self.a[t[3]:t[4]],
self.b[t[5]:t[6]])
else:
raise ValueError(what)
def merge_regions(self):
"""Return sequences of matching and conflicting regions.
This returns tuples, where the first value says what kind we
have:
'unchanged', start, end
Take a region of base[start:end]
'same', astart, aend
b and a are different from base but give the same result
'a', start, end
Non-clashing insertion from a[start:end]
Method is as follows:
The two sequences align only on regions which match the base
and both descendents. These are found by doing a two-way diff
of each one against the base, and then finding the
intersections between those regions. These "sync regions"
are by definition unchanged in both and easily dealt with.
The regions in between can be in any of three cases:
conflicted, or changed on only one side.
"""
# section a[0:ia] has been disposed of, etc
iz = ia = ib = 0
for (zmatch, zend, amatch, aend, bmatch,
bend) in self.find_sync_regions():
matchlen = zend - zmatch
# invariants:
# matchlen >= 0
# matchlen == (aend - amatch)
# matchlen == (bend - bmatch)
len_a = amatch - ia
len_b = bmatch - ib
# len_base = zmatch - iz
# invariants:
# assert len_a >= 0
# assert len_b >= 0
# assert len_base >= 0
# print 'unmatched a=%d, b=%d' % (len_a, len_b)
if len_a or len_b:
# try to avoid actually slicing the lists
same = compare_range(self.a, ia, amatch,
self.b, ib, bmatch)
if same:
yield 'same', ia, amatch
else:
equal_a = compare_range(self.a, ia, amatch,
self.base, iz, zmatch)
equal_b = compare_range(self.b, ib, bmatch,
self.base, iz, zmatch)
if equal_a and not equal_b:
yield 'b', ib, bmatch
elif equal_b and not equal_a:
yield 'a', ia, amatch
elif not equal_a and not equal_b:
if self.is_cherrypick:
for node in self._refine_cherrypick_conflict(
iz, zmatch, ia, amatch,
ib, bmatch):
yield node
else:
yield (
'conflict', iz, zmatch, ia, amatch, ib, bmatch)
else:
raise AssertionError(
"can't handle a=b=base but unmatched")
ia = amatch
ib = bmatch
iz = zmatch
# if the same part of the base was deleted on both sides
# that's OK, we can just skip it.
if matchlen > 0:
# invariants:
# assert ia == amatch
# assert ib == bmatch
# assert iz == zmatch
yield 'unchanged', zmatch, zend
iz = zend
ia = aend
ib = bend
def _refine_cherrypick_conflict(self, zstart, zend, astart, aend, bstart,
bend):
"""When cherrypicking b => a, ignore matches with b and base."""
# Do not emit regions which match, only regions which do not match
matcher = self.sequence_matcher(
None, self.base[zstart:zend], self.b[bstart:bend])
matches = matcher.get_matching_blocks()
last_base_idx = 0
last_b_idx = 0
last_b_idx = 0
yielded_a = False
for base_idx, b_idx, match_len in matches:
# conflict_z_len = base_idx - last_base_idx
conflict_b_len = b_idx - last_b_idx
# There are no lines in b which conflict, so skip it
if conflict_b_len == 0:
pass
else:
if yielded_a:
yield ('conflict',
zstart + last_base_idx, zstart + base_idx,
aend, aend, bstart + last_b_idx, bstart + b_idx)
else:
# The first conflict gets the a-range
yielded_a = True
yield (
'conflict', zstart + last_base_idx, zstart + base_idx,
astart, aend, bstart + last_b_idx, bstart + b_idx)
last_base_idx = base_idx + match_len
last_b_idx = b_idx + match_len
if last_base_idx != zend - zstart or last_b_idx != bend - bstart:
if yielded_a:
yield ('conflict', zstart + last_base_idx, zstart + base_idx,
aend, aend, bstart + last_b_idx, bstart + b_idx)
else:
# The first conflict gets the a-range
yielded_a = True
yield ('conflict', zstart + last_base_idx, zstart + base_idx,
astart, aend, bstart + last_b_idx, bstart + b_idx)
if not yielded_a:
yield ('conflict', zstart, zend, astart, aend, bstart, bend)
def reprocess_merge_regions(self, merge_regions):
"""Where there are conflict regions, remove the agreed lines.
Lines where both A and B have made the same changes are
eliminated.
"""
for region in merge_regions:
if region[0] != "conflict":
yield region
continue
type, iz, zmatch, ia, amatch, ib, bmatch = region
a_region = self.a[ia:amatch]
b_region = self.b[ib:bmatch]
matches = self.sequence_matcher(
None, a_region, b_region).get_matching_blocks()
next_a = ia
next_b = ib
for region_ia, region_ib, region_len in matches[:-1]:
region_ia += ia
region_ib += ib
reg = self.mismatch_region(next_a, region_ia, next_b,
region_ib)
if reg is not None:
yield reg
yield 'same', region_ia, region_len+region_ia
next_a = region_ia + region_len
next_b = region_ib + region_len
reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
if reg is not None:
yield reg
@staticmethod
def mismatch_region(next_a, region_ia, next_b, region_ib):
if next_a < region_ia or next_b < region_ib:
return 'conflict', None, None, next_a, region_ia, next_b, region_ib
def find_sync_regions(self):
"""Return a list of sync regions, where both descendents match the base.
Generates a list of (base1, base2, a1, a2, b1, b2). There is
always a zero-length sync region at the end of all the files.
"""
ia = ib = 0
amatches = self.sequence_matcher(
None, self.base, self.a).get_matching_blocks()
bmatches = self.sequence_matcher(
None, self.base, self.b).get_matching_blocks()
len_a = len(amatches)
len_b = len(bmatches)
sl = []
while ia < len_a and ib < len_b:
abase, amatch, alen = amatches[ia]
bbase, bmatch, blen = bmatches[ib]
# there is an unconflicted block at i; how long does it
# extend? until whichever one ends earlier.
i = intersect((abase, abase+alen), (bbase, bbase+blen))
if i:
intbase = i[0]
intend = i[1]
intlen = intend - intbase
# found a match of base[i[0], i[1]]; this may be less than
# the region that matches in either one
# assert intlen <= alen
# assert intlen <= blen
# assert abase <= intbase
# assert bbase <= intbase
asub = amatch + (intbase - abase)
bsub = bmatch + (intbase - bbase)
aend = asub + intlen
bend = bsub + intlen
# assert self.base[intbase:intend] == self.a[asub:aend], \
# (self.base[intbase:intend], self.a[asub:aend])
# assert self.base[intbase:intend] == self.b[bsub:bend]
sl.append((intbase, intend,
asub, aend,
bsub, bend))
# advance whichever one ends first in the base text
if (abase + alen) < (bbase + blen):
ia += 1
else:
ib += 1
intbase = len(self.base)
abase = len(self.a)
bbase = len(self.b)
sl.append((intbase, intbase, abase, abase, bbase, bbase))
return sl
def find_unconflicted(self):
"""Return a list of ranges in base that are not conflicted."""
am = self.sequence_matcher(
None, self.base, self.a).get_matching_blocks()
bm = self.sequence_matcher(
None, self.base, self.b).get_matching_blocks()
unc = []
while am and bm:
# there is an unconflicted block at i; how long does it
# extend? until whichever one ends earlier.
a1 = am[0][0]
a2 = a1 + am[0][2]
b1 = bm[0][0]
b2 = b1 + bm[0][2]
i = intersect((a1, a2), (b1, b2))
if i:
unc.append(i)
if a2 < b2:
del am[0]
else:
del bm[0]
return unc
m3 = Merge3(['common\n', 'base\n'],['common\n', 'a\n'],['common\n', 'b\n'])
print(list(m3.merge_annotated()))
# 威力彩電腦選號
# weleadlottery
from browser import document, html, alert
import random
try:
total = int(input("請問要出幾張威力彩卷號碼?"))
except:
alert("請輸入要選擇威力彩電腦選號數量的'整數'")
total = int(input("請問要出幾張威力彩卷號碼?"))
# 準備將電腦選出的號碼, 輸出到內定 id="brython_div" 的標註區域
output_div = document["brython_div"]
output_div <= "以下將出 " + str(total) + " 張威力彩電腦選號彩卷:" + html.BR()
for i in range(1, total + 1):
# 利用 list(range()) 產生第一區 1 到 38 , 第二區 1 到 8 的 population list
# 然後再透過 random.sample(population, k)
# 從 population, 產生 k 個不同的數字
section1_numbers = random.sample(list(range(1, 38)), 6)
section2_number = random.sample(list(range(1, 8)), 1)
output_div <= str(i) + ". 電腦選號第一區為: " + str(section1_numbers) + html.BR()
output_div <= ". 電腦選號第二區為: " + str(section2_number) + html.BR()
# from https://levelup.gitconnected.com/writing-tetris-in-python-2a16bddb5318
# 暫時關閉 system proxy 設定後, pip install pygame
#import pygame
import random
# 以下為 Brython 新增
from browser import document as doc
from browser import html
import browser.timer
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應
canvas = html.CANVAS(width = 400, height = 500, id="canvas")
brython_div = doc["brython_div"]
brython_div <= canvas
ctx = canvas.getContext("2d")
colors = [
(0, 0, 0),
(120, 37, 179),
(100, 179, 179),
(80, 34, 22),
(80, 134, 22),
(180, 34, 22),
(180, 34, 122),
]
class Figure:
x = 0
y = 0
figures = [
[[1, 5, 9, 13], [4, 5, 6, 7]],
[[4, 5, 9, 10], [2, 6, 5, 9]],
[[6, 7, 9, 10], [1, 5, 6, 10]],
[[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]],
[[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]],
[[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]],
[[1, 2, 5, 6]],
]
def __init__(self, x, y):
self.x = x
self.y = y
self.type = random.randint(0, len(self.figures) - 1)
self.color = random.randint(1, len(colors) - 1)
self.rotation = 0
def image(self):
return self.figures[self.type][self.rotation]
def rotate(self):
self.rotation = (self.rotation + 1) % len(self.figures[self.type])
class Tetris:
level = 2
score = 0
state = "start"
field = []
height = 0
width = 0
x = 100
y = 60
zoom = 20
figure = None
def __init__(self, height, width):
self.height = height
self.width = width
self.field = []
self.score = 0
self.state = "start"
for i in range(height):
new_line = []
for j in range(width):
# 起始時每一個都填入 0
new_line.append(0)
self.field.append(new_line)
def new_figure(self):
self.figure = Figure(3, 0)
def intersects(self):
intersection = False
for i in range(4):
for j in range(4):
if i * 4 + j in self.figure.image():
# block 到達底部, 左右兩邊界, 或該座標有其他 block
if i + self.figure.y > self.height - 1 or \
j + self.figure.x > self.width - 1 or \
j + self.figure.x < 0 or \
self.field[i + self.figure.y][j + self.figure.x] > 0:
intersection = True
return intersection
def break_lines(self):
lines = 0
for i in range(1, self.height):
zeros = 0
for j in range(self.width):
if self.field[i][j] == 0:
zeros += 1
if zeros == 0:
lines += 1
for i1 in range(i, 1, -1):
for j in range(self.width):
self.field[i1][j] = self.field[i1 - 1][j]
self.score += lines ** 2
def go_space(self):
while not self.intersects():
self.figure.y += 1
self.figure.y -= 1
self.freeze()
def go_down(self):
self.figure.y += 1
if self.intersects():
self.figure.y -= 1
self.freeze()
def freeze(self):
for i in range(4):
for j in range(4):
if i * 4 + j in self.figure.image():
self.field[i + self.figure.y][j + self.figure.x] = self.figure.color
self.break_lines()
self.new_figure()
if self.intersects():
self.state = "gameover"
def go_side(self, dx):
old_x = self.figure.x
self.figure.x += dx
if self.intersects():
self.figure.x = old_x
def rotate(self):
old_rotation = self.figure.rotation
self.figure.rotate()
if self.intersects():
self.figure.rotation = old_rotation
# Define some colors
# from https://stackoverflow.com/questions/3380726/converting-a-rgb-color-tuple-to-a-six-digit-code
BLACK = '#%02x%02x%02x' % (0, 0, 0)
WHITE = '#%02x%02x%02x' % (255, 255, 255)
GRAY = '#%02x%02x%02x' % (128, 128, 128)
done = False
fps = 25
game = Tetris(20, 10)
counter = 0
pressing_down = False
def key_down(eve):
key = eve.keyCode
#if event.type == pygame.QUIT:
# 32 is pause
if key == 32:
done = True
# 82 is r key to rotate
if key == 82:
game.rotate()
# 40 is down key
if key == 40:
pressing_down = True
# 37 is left key
if key == 37:
game.go_side(-1)
# 39 is right key
if key == 39:
game.go_side(1)
# 68 is d key to move block to bottom
if key == 68:
game.go_space()
# 27 is escape
# reset the game
if key == 27:
game.__init__(20, 10)
def key_up(eve):
key = eve.keyCode
# 40 is down key
if key == 40:
pressing_down = False
#while not done:
def do_game():
global counter
if game.figure is None:
game.new_figure()
counter += 1
if counter > 100000:
counter = 0
if counter % (fps // game.level // 2) == 0 or pressing_down:
if game.state == "start":
game.go_down()
for i in range(game.height):
for j in range(game.width):
ctx.fillStyle = WHITE
#ctx.scale(game.zoom, game.zoom)
ctx.fillRect(game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom)
if game.field[i][j] > 0:
ctx.fillStyle = '#%02x%02x%02x' % colors[game.field[i][j]]
ctx.fillRect(game.x + game.zoom * j + 1, game.y + game.zoom * i + 1, game.zoom - 2, game.zoom - 1)
ctx.lineWidth = 1
ctx.strokeStyle = GRAY
ctx.beginPath()
ctx.rect(game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom)
ctx.stroke()
if game.figure is not None:
for i in range(4):
for j in range(4):
p = i * 4 + j
if p in game.figure.image():
ctx.fillStyle = '#%02x%02x%02x' % colors[game.figure.color]
ctx.fillRect(game.x + game.zoom * (j + game.figure.x) + 1,
game.y + game.zoom * (i + game.figure.y) + 1,
game.zoom - 2, game.zoom - 2)
doc.addEventListener("keydown", key_down)
doc.addEventListener("keyup", key_up)
browser.timer.set_interval(do_game, fps)
# Cango gearUtils-0.9.js Spur Gears
from browser import document as doc
from browser import html
from browser import window
import browser.timer
import math
canvas = html.CANVAS(width = 600, height = 400)
canvas.id = "cango_gear"
brython_div = doc["brython_div"]
brython_div <= canvas
canvas = doc["cango_gear"]
# 此程式採用 Cango Javascript 程式庫繪圖, 因此無需 ctx
#ctx = canvas.getContext("2d")
cango = window.Cango.new
path = window.Path.new
creategeartooth = window.createGearTooth.new
circle = window.circle.new
svgsegs = window.SVGsegs.new
# 經由 Cango 轉換成 Brython 的 cango, 指定將圖畫在 id="cango_gear" 的 canvas 上
cgo = cango("cango_gear")
######################################
# 畫正齒輪輪廓
#####################################
def cangoGear(n, m, pa, x=0, y=0, color="#606060"):
# n 為齒數
#n = 17
# pa 為壓力角
#pa = 25
# m 為模數, 根據畫布的寬度, 計算適合的模數大小
# Module = mm of pitch diameter per tooth
#m = 0.8*canvas.width/n
# pr 為節圓半徑
pr = n*m/2 # gear Pitch radius
# generate gear data
data = creategeartooth(m, n, pa)
toothSVG = svgsegs(data)
toothSVG.rotate(180/n) # rotate gear 1/2 tooth to mesh
# 單齒的齒形資料經過旋轉後, 將資料複製到 gear 物件中
one = toothSVG.dup()
# 利用單齒輪廓旋轉, 產生整個正齒輪外形
for i in range(1, n):
newSVG = one.rotate(360*i/n)
toothSVG = toothSVG.appendPath(newSVG)
# 建立軸孔
# add axle hole, hr 為 hole radius
hr = 0.6*pr # diameter of gear shaft
shaft = circle(hr)
shaftSVG = svgsegs(shaft)
spurSVG = toothSVG.appendPath(shaftSVG)
gear = path(spurSVG, {"x": x, "y": y, "strokeColor": color})
return gear
# 設定兩齒齒數
n1 = 84
n2 = 18
n3 = 99
# 使用 80% 的畫布寬度
m = 0.8*canvas.width/((n1+n2+n3))
# 設定共同的壓力角
pa = 25
# n 齒輪的節圓半徑
pr1 = n1*m/2
# n2 齒輪的節圓半徑
pr2 = n2*m/2
pr3 = n3*m/2
cx = canvas.width/2
cy = canvas.height/2
# Determine the coord of the middle gears
mcx = cx + (pr1-pr3)
mcy = cy
# 建立 gears
gear1 = cangoGear(n1, m, pa, color="red")
gear2 = cangoGear(n2, m, pa, color="green")
gear3 = cangoGear(n3, m, pa, color="blue")
deg = math.pi/180
rotate_speed = 0
def draw():
global rotate_speed
rotate_speed += 5*deg
cgo.clearCanvas()
theta1 = 0+rotate_speed
gear1.rotate(theta1)
gear1.translate(mcx-(pr1+pr2), mcy)
cgo.render(gear1)
theta2 = 180+(360/n2/2)-(rotate_speed)*n1/n2
gear2.rotate(theta2)
gear2.translate(mcx, mcy)
cgo.render(gear2)
theta3 = 180+(360/n3/2)+(180+(360/n2/2))*n2/n3+(rotate_speed*n1/n2)*(n2/n3)
gear3.rotate(theta3)
gear3.translate(mcx+(pr2+pr3), mcy)
cgo.render(gear3)
browser.timer.set_interval(draw, 2)
# Draw single Spur Gear
from browser import document as doc
from browser import html
import math
# 利用 Brython 建立 canvas 標註元件
canvas = html.CANVAS(width = 600, height = 400)
# 將此 canvas 的 id 設為 "spur"
canvas.id = "gear"
# 將 brython_div 變數設為 id 為 "brython_div 的 doc 物件
brython_div = doc["brython_div"]
# 將此 canvas 物件插入網頁
brython_div <= canvas
# 利用 canvas 代表 id="spur" 標註元件
# 表示要將 ctx 2d 繪圖至 canvas
canvas = doc["gear"]
ctx = canvas.getContext("2d")
# 插入輸入表單
form = html.FORM()
gearNumInput = html.INPUT(type="text", id="gearnum", value="23")
button = html.BUTTON("設定齒數", id="set_num")
form <= "齒數: " + gearNumInput + html.BR()
brython_div <= form + button + html.BR()
#print(html.BUTTON("設定齒數", id="set_num").outerHTML)
# 以下建立正齒輪繪圖物件與設定齒數函式
# deg 為角度轉為徑度的轉換因子
deg = math.pi/180.
# 定義 Spur 類別
class Spur:
def __init__(self, ctx):
self.ctx = ctx
def create_line(self, x1, y1, x2, y2, width=1, fill="red"):
self.ctx.beginPath()
self.ctx.lineWidth = width
self.ctx.moveTo(x1, y1)
self.ctx.lineTo(x2, y2)
self.ctx.strokeStyle = fill
self.ctx.stroke()
# 定義一個繪正齒輪的繪圖函式
# midx 為齒輪圓心 x 座標
# midy 為齒輪圓心 y 座標
# rp 為節圓半徑, n 為齒數
# pa 為壓力角 (deg)
# rot 為旋轉角 (deg)
# 針對 n 大於等於 52 齒時 base circle 與齒根圓大小必須進行判斷
def Gear(self, midx, midy, rp, n=20, pa=20, color="black"):
# 齒輪漸開線分成 15 線段繪製
imax = 15
# 在輸入的畫布上繪製直線, 由圓心到節圓 y 軸頂點畫一直線
self.create_line(midx, midy, midx, midy-rp)
# a 為模數 (代表公制中齒的大小), 模數為節圓直徑(稱為節徑)除以齒數
# 模數也就是齒冠大小
a=2*rp/n
# d 為齒根大小, 為模數的 1.157 或 1.25倍, 這裡採 1.25 倍
d=2.5*rp/n
# ra 為齒輪的外圍半徑
ra=rp+a
# rb 則為齒輪的基圓半徑
# 基圓為漸開線長齒之基準圓
rb=rp*math.cos(pa*deg)
# rd 為齒根圓半徑
rd=rp-d
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd
# dr 則為基圓到齒頂圓半徑分成 imax 段後的每段半徑增量大小
# 將圓弧分成 imax 段來繪製漸開線
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd
if rd>rb:
dr = (ra-rd)/imax
else:
dr=(ra-rb)/imax
# tan(pa*deg)-pa*deg 為漸開線函數
sigma=math.pi/(2*n)+math.tan(pa*deg)-pa*deg
for j in range(n):
ang=-2.*j*math.pi/n+sigma
ang2=2.*j*math.pi/n+sigma
lxd=midx+rd*math.sin(ang2-2.*math.pi/n)
lyd=midy-rd*math.cos(ang2-2.*math.pi/n)
for i in range(imax+1):
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd
if rd>rb:
r=rd+i*dr
else:
r=rb+i*dr
theta=math.sqrt((r*r)/(rb*rb)-1.)
alpha=theta-math.atan(theta)
xpt=r*math.sin(alpha-ang)
ypt=r*math.cos(alpha-ang)
xd=rd*math.sin(-ang)
yd=rd*math.cos(-ang)
# i=0 時, 繪線起點由齒根圓上的點, 作為起點
if(i==0):
last_x = midx+xd
last_y = midy-yd
# 由左側齒根圓作為起點, 除第一點 (xd,yd) 齒根圓上的起點外, 其餘的 (xpt,ypt)則為漸開線上的分段點
self.create_line((midx+xpt),(midy-ypt),(last_x),(last_y),fill=color)
# 最後一點, 則為齒頂圓
if(i==imax):
lfx=midx+xpt
lfy=midy-ypt
last_x = midx+xpt
last_y = midy-ypt
# the line from last end of dedendum point to the recent
# end of dedendum point
# lxd 為齒根圓上的左側 x 座標, lyd 則為 y 座標
# 下列為齒根圓上用來近似圓弧的直線
self.create_line((lxd),(lyd),(midx+xd),(midy-yd),fill=color)
for i in range(imax+1):
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd
if rd>rb:
r=rd+i*dr
else:
r=rb+i*dr
theta=math.sqrt((r*r)/(rb*rb)-1.)
alpha=theta-math.atan(theta)
xpt=r*math.sin(ang2-alpha)
ypt=r*math.cos(ang2-alpha)
xd=rd*math.sin(ang2)
yd=rd*math.cos(ang2)
# i=0 時, 繪線起點由齒根圓上的點, 作為起點
if(i==0):
last_x = midx+xd
last_y = midy-yd
# 由右側齒根圓作為起點, 除第一點 (xd,yd) 齒根圓上的起點外, 其餘的 (xpt,ypt)則為漸開線上的分段點
self.create_line((midx+xpt),(midy-ypt),(last_x),(last_y),fill=color)
# 最後一點, 則為齒頂圓
if(i==imax):
rfx=midx+xpt
rfy=midy-ypt
last_x = midx+xpt
last_y = midy-ypt
# lfx 為齒頂圓上的左側 x 座標, lfy 則為 y 座標
# 下列為齒頂圓上用來近似圓弧的直線
self.create_line(lfx,lfy,rfx,rfy,fill=color)
# 以 button 驅動的事件函式
def setgearnumber(e):
global ctx
ctx.clearRect(0, 0, canvas.width, canvas.height)
x = (canvas.width)/2
y = (canvas.height)/2
if doc["gearnum"].value.isdigit():
n1 = int(doc["gearnum"].value)
else:
n1= 26
# 設定齒輪參數
x = (canvas.width)/2
y = (canvas.height)/2
r = 0.8*(canvas.height/2)
pa = 20
# 繪出正齒輪
spur = Spur(ctx)
spur.Gear(x, y, r, n1, pa, "blue")
#判定 button
setgearnumber(True)
doc['set_num'].bind('click',setgearnumber)
# try to generate binary or ascii stl part file and shown on browser - not complete yet
import struct
class StLFacet:
def __init__(self, normal, v1, v2, v3, att_bc=0):
self.coords = [normal, v1, v2, v3]
self.att_bc = att_bc
class StL:
def __init__(self, header):
self.header = header
self.facets = []
def add_facet(self, facet):
self.facets.append(facet)
def get_binary(self):
# 原先 2.0 的版本
#out = ['%-80.80s' % self.header]
# 改為 Python 3.0 格式
# 第一行標頭的格式
header = ['%-80.80s' % self.header][0]
# 利用 bytes() 將標頭字串轉為二位元資料
out = [bytes(header,encoding="utf-8")]
# 接著則計算三角形面的數量, 並以二位元長整數格式存檔
out.append(struct.pack('L',len(self.facets)))
# 接著則依照法線向量與三個點座標的格式, 分別以浮點數格式進行資料附加
for f in self.facets:
for coord in f.coords:
out.append(struct.pack('3f', *coord))
# att_bc 則內定為 0
out.append(struct.pack('H', f.att_bc))
return b"".join(out)
def test():
stl=StL('Header ...')
stl.add_facet(StLFacet((0.,0.,1.),(0.,0.,0.),(1.,0.,0.),(0.,1.,0.)))
stl.add_facet(StLFacet((0.,0.,1.),(1.,0.,0.),(1.,1.,0.),(0.,1.,0.)))
# 第二個平面
stl.add_facet(StLFacet((0.,-1.,0.),(0.,0.,0.),(0.,0.,-1.),(1.,0.,-1.)))
stl.add_facet(StLFacet((0.,-1.,0.),(0.,0.,0.),(1.,0.,-1.),(1.,0.,0.)))
return stl.get_binary()
# 指定存為 binary 格式檔案
#stlfile = open("test.stl", "wb")
stlcontent = test()
#stlfile.write(stlcontent)
# 以下將 binary stlcontent 轉為 ASCII stl
normals = []
points = []
triangles = []
triangle_number = 0
def load_binary_stl(fp):
'''
二位元 STL 檔案格式如下:
檔案標頭共有 80 個字元(bytes), 內容通常省略, 但是內容不可使用 solid, 以免與文字檔案 STL 混淆
UINT8[80] – Header
UINT32 – Number of triangles (I:佔 4 bytes 的 unsigned integer)
foreach triangle
REAL32[3] – Normal vector (f:每一座標分量為一佔 4 bytes 的 float, 共佔 12 bytes)
REAL32[3] – Vertex 1
REAL32[3] – Vertex 2
REAL32[3] – Vertex 3
UINT16 – Attribute byte count (H:兩個 bytes 的 unsigned short, 表示 attribute byte count)
end
'''
# 已經在外部開檔
#fp=open(filename,'rb')
header=fp.read(80)
triangle_number = struct.unpack('I',fp.read(4))[0]
#print(triangle_number)
count=0
while True:
try:
p=fp.read(12)
if len(p)==12:
n=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
normals.append(n)
l = len(points)
#print(n)
p=fp.read(12)
if len(p)==12:
p1=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
points.append(p1)
#print(p1)
p=fp.read(12)
if len(p)==12:
p2=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
points.append(p2)
p=fp.read(12)
if len(p)==12:
p3=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
points.append(p3)
triangles.append((l, l+1, l+2))
# 使用 count 來計算三角形平面個數
# triangle_number 為 STL 檔案中的三角形個數
count += 1
#print(count)
# 在前面 12*4 個 bytes 的 normal 與三個點資料後, 為
# 一個 2 bytes 長的 unsigned short, 其值為零, 為 attribute
fp.read(2)
# 讀完所有三角平面後, 即跳出 while
if count > triangle_number:
break
except EOFError:
break
#fp.close()
def read_length(f):
length = struct.unpack("@i", f.read(4))
return length[0]
def read_header(f):
f.seek(f.tell()+80)
def write_as_ascii(outfilename):
f = open(outfilename, "w")
f.write ("solid "+outfilename+"\n")
for n in range(len(triangles)):
f.write ("facet normal {} {} {}\n".format(normals[n][0],normals[n][1],normals[n][2]))
f.write ("outer loop\n")
f.write ("vertex {} {} {}\n".format(points[triangles[n][0]][0],points[triangles[n][0]][1],points[triangles[n][0]][2]))
f.write ("vertex {} {} {}\n".format(points[triangles[n][1]][0],points[triangles[n][1]][1],points[triangles[n][1]][2]))
f.write ("vertex {} {} {}\n".format(points[triangles[n][2]][0],points[triangles[n][2]][1],points[triangles[n][2]][2]))
f.write ("endloop\n")
f.write ("endfacet\n")
f.write ("endsolid "+outfilename+"\n")
f.close()
infilename = "ss1.stl"
outfilename = "ss2.stl"
try:
f = open(infilename, "rb")
#read_header(f)
#l = read_length(f)
try:
load_binary_stl(f)
l = len(normals)
except Exception as e:
print("Exception",e)
print(len(normals), len(points), len(triangles), l)
write_as_ascii(outfilename)
print("done")
except Exception as e:
print(e)
# http://mde.tw/2016fallcadp/blog/2016fall-dian-nao-fu-zhu-she-ji-shi-xi-ke-cheng-zong-jie-yi.html
#coding:utf-8
# source: http://code.activestate.com/recipes/578246-stl-writer/
import struct
ASCII_FACET = """facet normal 0 0 0
outer loop
vertex {face[0][0]:.4f} {face[0][1]:.4f} {face[0][2]:.4f}
vertex {face[1][0]:.4f} {face[1][1]:.4f} {face[1][2]:.4f}
vertex {face[2][0]:.4f} {face[2][1]:.4f} {face[2][2]:.4f}
endloop
endfacet
"""
BINARY_HEADER ="80sI"
BINARY_FACET = "12fH"
class ASCII_STL_Writer:
""" Export 3D objects build of 3 or 4 vertices as ASCII STL file.
"""
def __init__(self, stream):
self.fp = stream
self._write_header()
def _write_header(self):
self.fp.write("solid python\n")
def close(self):
self.fp.write("endsolid python\n")
def _write(self, face):
self.fp.write(ASCII_FACET.format(face=face))
def _split(self, face):
p1, p2, p3, p4 = face
return (p1, p2, p3), (p3, p4, p1)
def add_face(self, face):
""" Add one face with 3 or 4 vertices. """
if len(face) == 4:
face1, face2 = self._split(face)
self._write(face1)
self._write(face2)
elif len(face) == 3:
self._write(face)
else:
raise ValueError('only 3 or 4 vertices for each face')
def add_faces(self, faces):
""" Add many faces. """
for face in faces:
self.add_face(face)
class Binary_STL_Writer(ASCII_STL_Writer):
""" Export 3D objects build of 3 or 4 vertices as binary STL file.
"""
def __init__(self, stream):
self.counter = 0
super(Binary_STL_Writer, self).__init__(stream)
def close(self):
self._write_header()
def _write_header(self):
self.fp.seek(0)
self.fp.write(struct.pack(BINARY_HEADER, b'Python Binary STL Writer', self.counter))
def _write(self, face):
self.counter += 1
data = [
0., 0., 0.,
face[0][0], face[0][1], face[0][2],
face[1][0], face[1][1], face[1][2],
face[2][0], face[2][1], face[2][2],
0
]
self.fp.write(struct.pack(BINARY_FACET, *data))
def example():
def get_cube():
# cube corner points
s = 3.
p1 = (0, 0, 0)
p2 = (0, 0, s)
p3 = (0, s, 0)
p4 = (0, s, s)
p5 = (s, 0, 0)
p6 = (s, 0, s)
p7 = (s, s, 0)
p8 = (s, s, s)
# define the 6 cube faces
# faces just lists of 3 or 4 vertices
return [
[p1, p5, p7, p3],
[p1, p5, p6, p2],
[p5, p7, p8, p6],
[p7, p8, p4, p3],
[p1, p3, p4, p2],
[p2, p6, p8, p4],
]
'''
for writing ASCII STL cube file
with open('cube_ascii.stl', 'w') as fp:
writer = ASCII_STL_Writer(fp)
writer.add_faces(get_cube())
writer.close()
'''
with open('cube_bin.stl', 'wb') as fp:
writer = Binary_STL_Writer(fp)
writer.add_faces(get_cube())
writer.close()
if __name__ == '__main__':
example()
# Turtle1 繪圖
from browser import window, html
from browser import document as doc
import turtle
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
t = turtle.Turtle()
t.width(5)
for c in ['red', '#00ff00', '#fa0', 'rgb(0,0,200)']:
t.color(c)
t.forward(100)
t.left(90)
# dot() and write() do not require the pen to be down
t.penup()
t.goto(-30, -100)
t.dot(40, 'rgba(255, 0, 0, 0.5')
t.goto(30, -100)
t.dot(40, 'rgba(0, 255, 0, 0.5')
t.goto(0, -70)
t.dot(40, 'rgba(0, 0, 255, 0.5')
t.goto(0, 125)
t.color('purple')
t.write("這就是 Brython, 網頁上的 Python", font=("Arial", 15, "normal"))
turtle.done()
# Turtle2 繪圖
from browser import document as doc
import turtle
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
star = turtle.Turtle()
for i in range(5):
star.forward(250)
star.right(144)
turtle.done()
# Turtle3 繪圖
# https://michael0x2a.com/blog/turtle-examples
from browser import document as doc
import turtle
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
painter = turtle.Turtle()
painter.pencolor("blue")
for i in range(50):
painter.forward(50)
painter.left(123) # Let's go counterclockwise this time
painter.pencolor("red")
for i in range(50):
painter.forward(100)
painter.left(123)
turtle.done()
# Turtle4 繪圖
# https://docs.python.org/3.7/library/turtle.html?highlight=turtle
# https://fiftyexamples.readthedocs.io/en/latest/turtle.html
from browser import document as doc
import turtle
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
# 輸入 "turtle" 字串, 表示要使用內建的烏龜圖示
t = turtle.Turtle("turtle")
# 設定繪圖尺寸
screen_x = 500-20
screen_y = 300
# 提筆, 將烏龜移動到畫布中心
t.penup()
t.home()
# 內定方向為右, 前進 screen_x/2
t.forward(screen_x / 2)
# 將方向往右轉 90 度
t.right(90)
# 此時方向向下, 前進 screen_y/2
t.forward(screen_y / 2)
# 令烏龜方向轉絕對角度 180, 等同轉相對角度 90 度, 即 t.right(90)
t.setheading(180)
# 將畫筆顏色設為紅色
t.pencolor('red')
# 下筆準備繪圖
t.pendown()
# 設筆寬度為 10
t.pensize(10)
# 進入重複迴圈, 此時方向向右, 分別
# 前進 screen_x, 之後轉 90 度, 方向朝上
# 再前進 screen_y, 之後再轉 90 度, 方向朝右
# 再前進 screen_x, 之後轉 90 度, 方向朝下
# 最後再前進 screen_y 後, 將方向轉為向左
for distance in (screen_x, screen_y, screen_x, screen_y):
t.forward(distance)
t.right(90)
# 提筆後, 將烏龜轉回內定方向回到畫布中心
t.penup()
t.home()
# 完成 turtle 繪圖
turtle.done()
# Turtle5 繪圖
# https://brython.info/gallery/turtle.html
from browser import document as doc
import turtle
import math
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
t = turtle.Turtle("turtle")
t.speed(1)
t.forward(50)
print("Should be (50, 0):", t.position())
print("Should be 50: ", t.xcor())
print("Should be 0: ", t.ycor())
t.left(90)
t.color("blue")
t.speed(2)
t.fd(50)
print("Should be (50, 50):", t.pos())
print("Should be 225: ", t.towards(0, 0))
print("Should be 90: ", t.heading())
print("Should be approximately 71:", t.distance(0, 0))
# Draw the same square in three different angle modes
t.width(4)
print("Drawing using degrees - the default")
print("Heading should be 90: ", t.heading())
for i in range(4):
t.forward(100)
t.left(90)
print("Drawing using radians")
t.radians()
print("Heading should be pi/2: ", t.heading())
for i in range(4):
t.forward(100)
t.left(math.pi/2)
print("Drawing using gradients")
t.degrees(400)
print("Heading should be 100: ", t.heading())
for i in range(4):
t.forward(100)
t.left(100)
t.degrees()
t.width(1)
t.lt(90)
t.color("orange")
t.backward(50)
t.right(90)
t.color("green")
t.back(50)
t.rt(90)
t.color("red")
t.bk(50)
t.stamp()
t.speed(4)
t.color("black", "white")
t.goto(-100, 100)
t.stamp()
t.color("blue", "yellow")
t.setposition(0, 100)
t.stamp()
t.color("green", "white")
t.setpos(100, 100)
t.stamp()
t.speed(10)
t.color("orange")
t.sety(-100)
t.setx(-100)
t.stamp()
t.color("cyan")
t.home()
t.stamp()
t.color("green")
t.width(4)
t.setheading(180)
t.forward(150)
t.seth(90)
t.fd(20)
t.dot(30, "rgba(255, 0, 0, 0.2)")
t.color("red")
t.speed(0)
t.forward(30)
t.left(90)
t.circle(30)
turtle.done()
# Turtle6 繪圖
from browser import document as doc
import turtle
import math
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
t = turtle.Turtle("turtle")
t.speed(10)
colors = ['red', 'purple', 'blue', 'green', 'orange']
for x in range(150):
t.pencolor(colors[x % 5])
t.width(x/10 + 1)
t.forward(x)
t.left(59)
turtle.done()
# Turtle7 繪圖
from browser import document as doc
import turtle
import math
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
screen=turtle.Screen() #making a canvas for drawing
screen.bgcolor('black') #making canvas black
trtl=turtle.Turtle() #making a turtle
trtl.pencolor('red') #making colour of the pen red
trtl.pensize(5) #choosing the size of pen nib
trtl.speed(10) #choosing the speed of drawing
# shape should be ‘arrow’, ‘classic’, ‘turtle’ or ‘circle’
trtl.shape('turtle') #choosing the shape of pen nib
trtl.forward(150) #drawing a line of 150 pixels
trtl.right(90) #asking turtle to turn 90 degrees
trtl.forward(150) #drawing a line of 150 pixels
trtl.penup() # preparing for moving pen without drawing
trtl.setpos(-140,-120) # making the new position of the turtle
trtl.pendown() # bringing the pen down for drawing again
trtl.pencolor('green') # choosin the pen colour as green
trtl.write('Brython 烏龜繪圖', font=("Arial", 20, "bold")) # chosing the font
trtl.penup()
trtl.ht() # hiding the turtle from the screen
turtle.done()
# Turtle8 繪圖
from browser import document as doc
import turtle
import math
turtle.set_defaults(
turtle_canvas_wrapper = doc['brython_div']
)
wn = turtle.Screen()
wn.bgcolor("black")
skk = turtle.Turtle()
skk.speed(10)
skk.shape("circle")
skk.color("blue")
def sqrfunc(size):
for i in range(4):
skk.fd(size)
skk.left(90)
size = size-5
sqrfunc(146)
sqrfunc(126)
sqrfunc(106)
sqrfunc(86)
sqrfunc(66)
sqrfunc(46)
sqrfunc(26)
turtle.done()
# 2a w3 grouping program, 與 2b 處理架構相同
from browser import html
from browser import document
import random
brython_div = document["brython_div"]
# 根據 href 與 content 將 html 元件中的 anchor 插入頁面
def makeLink(href, content):
brython_div <= html.A(content, href=href)
#brython_div <= html.BR()
aorb = "a"
url = "https://mde.tw/studlist/2022spring/2a.txt"
course = "cd2022"
# 從 url 讀取資料後, 以跳行符號分割資料進入數列後
# 去除數列中的第一筆與最後一筆資料後可得每位學員所填的資料
data = open(url).read().split("\n")[1:-1]
#print(data)
# 再以 \t 分割每位學員的資料,
#可以取得每位學員的學號, github 帳號與組別
big = []
num_github = {}
num_grp = {}
for i in data:
stud_no, github, grp_no = i.split("\t")
#print(stud_no, github, grp_no)
big.append([stud_no, github, grp_no])
if github != "":
num_github[stud_no] = github
else:
num_github[stud_no] = stud_no
num_grp[stud_no] = grp_no
#print(big)
# 根據每一 element 的第三個 element sort
big.sort(key = lambda x: x[2])
# big 已經按照組別排序
#print(big)
grouped = []
for i in big:
grouped.append([i[2], i[0]])
#print(grouped)
d = {}
# 逐一檢視 grouped 數列
for i in grouped:
# 若該組序已存在 d dict 中,
# 則以 extend() 納入除組序之外的組員學號
if i[0] in d:
d[i[0]].extend(i[1:])
#print("i[0] in d:",i[0], "d:", d)
else:
# 若已納分組的 element 中之組序為全新組序,
# 則將該已納分組的 element 放入 dict 首位元素
# 準備透過 extend() 納入其他組員學號
d[i[0]] = i
#print("i i[0] not in d:", i, "d:", d)
#print("finally d:", d, "d.values():", d.values())
group_member = list(d.values())
# group_member 第一位為組序, 隨後為組員學號
#print(group_member)
for i in group_member:
brython_div <= "第" + str(i[0]) + "組:" + html.BR()
grp_repo = course + aorb + "g" + str(i[0])
for num in i[1:]:
# num 為各組組員學號
#print(num)
studhref = "https://"+ str(num_github[num]) + ".github.io/" + course
repohref = "https://github.com/"+ str(num_github[num]) +"/"+course
grphref = "https://"+ str(num_github[num]) + ".github.io/" + grp_repo
grp_repohref = "https://github.com/"+ str(num_github[num]) +"/" + grp_repo
brython_div <= "repo: "
makeLink(repohref, str(num))
brython_div <= " www: "
makeLink(studhref, str(num))
brython_div <= " " + grp_repo + "-repo: "
makeLink(grp_repohref, str(num))
brython_div <= " " + grp_repo + "-www: "
makeLink(grphref, str(num))
brython_div <= html.BR()
print("done")
# 參考: https://usefulangle.com/post/352/javascript-capture-image-from-camera
# 參考: https://usefulangle.com/post/353/javascript-canvas-image-upload
from browser import html, document, window
# 建立 video tag, 並且插入既有的 brython_div 標註中
# autoplay=True for phone camera, 480X640 for iphone
videotag = html.VIDEO(autoplay=True,id='video', width='480', height='640')
# 建立 canvas tag
canvas = html.CANVAS(width = 480, height = 640, id="canvas")
# 建立 button tag
button = html.BUTTON("Save Image", id="save")
# 分別將上列 tags 插入 id="brython_div" 之位置
brython_div = document['brython_div']
brython_div <= videotag + html.BR()
brython_div <= button + html.BR()
brython_div <= canvas
ctx = canvas.getContext("2d")
# 定義 OnSuccess 函式在 video 標註中播放 Webcam 串流影像
def OnSuccess(stream):
video = document['video']
video.srcObject = stream
video.play()
# 透過瀏覽器取得使用者同意後傳送 Webcam 串流資料
# 'facingMode': 'environment' for rear camera
window.navigator.mediaDevices.getUserMedia(
{"video": {"facingMode": 'environment'}, "audio": False}
).then(OnSuccess)
# 建立儲存串流影像至圖檔函式
def save_image(e):
#print(canvas.width)
video = document['video']
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
image_data_url = canvas.toDataURL('image/jpeg')
print(image_data_url)
# click id="save" 按鈕後, 執行 save_image
# 此 image_data_url 可以用於將影像檔案送到 server
document["save"].bind("click", save_image)
# Brython websocket example
"""
# Python WebSocket server
# pip install asyncio websockets
import asyncio
import websockets
async def hello(websocket, path):
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
"""
from browser import document
from browser import html, window, websocket
# 將 brython_div 變數設為 id 為 "brython_div 的 doc 物件
brython_div = document["brython_div"]
# 插入輸入表單
form = html.FORM()
gearNumInput = html.INPUT(type="text", id="data", value="特斯拉")
button = html.BUTTON("Run Websocket", id="run")
button3 = html.BUTTON("Close Connection", id="close")
form <= "Name: " + gearNumInput + html.BR()
brython_div <= form + button + html.BR()
brython_div <= button3 + html.BR()
def on_open(evt):
# Web Socket is connected, send data using send()
data = document["data"].value
if data:
ws.send(data)
alert("Message is sent")
def on_message(evt):
# message received from server
alert("Message received : %s" %evt.data)
def on_close(evt):
# websocket is closed
alert("Connection is closed")
ws = None
def _test(_):
global ws
# open a web socket
# ws 為一般連線, wss 為 secure 連線
ws = websocket.WebSocket("ws://localhost:8765")
# attach functions to web sockets events
ws.bind("open", on_open)
ws.bind("message", on_message)
ws.bind("close", on_close)
def close_connection(_):
ws.close()
def hello(ev):
alert("Hello !")
# 假如不導入 websocket, 也可以執行 Javascript 的 WebSocket
#websocket = window.WebSocket.new
document["run"].bind("click", _test)
document["close"].bind("click", close_connection)
# Ycombinator quicksort example
Y = lambda f: lambda *args: f(Y(f))(*args)
quicksort = Y(lambda f:
lambda x: (
f([item for item in x if item < x[0]])
+ [y for y in x if x[0] == y]
+ f([item for item in x if item > x[0]])
) if x else [])
print(quicksort([1, 3, 5, 4, 1, 3, 2]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment