def translation(self, rel): origin_sp1 = self.origin_sp + Vector(rel) x_interval = (config["win_width"] / 2 - config["x_max"] * self.unit_screen, config["win_width"] / 2) y_interval = (config["win_height"] / 2, config["win_height"] / 2 + config["y_max"] * self.unit_screen) x1 = clip(origin_sp1[0], *x_interval) y1 = clip(origin_sp1[1], *y_interval) self.origin_sp = Vector((x1, y1))
def zoom(self, pos, rate): pos = Vector(pos) unit_screen1 = clip(self.unit_screen * rate, *config["camera.unit_screen.interval"]) rate1 = unit_screen1 / self.unit_screen self.unit_screen = unit_screen1 origin_sp1 = pos + (self.origin_sp - pos) * rate1 x_interval = (config["win_width"] / 2 - config["x_max"] * self.unit_screen, config["win_width"] / 2) y_interval = (config["win_height"] / 2, config["win_height"] / 2 + config["y_max"] * self.unit_screen) x1 = clip(origin_sp1[0], *x_interval) y1 = clip(origin_sp1[1], *y_interval) self.origin_sp = Vector((x1, y1))
def add_gen(cls, name: str, deg, deg3d=(0, 0, 0)): """Add a new generator and return it.""" if cls.pred(deg3d): index = cls.generators[-1][0] + 1 if cls.generators else 0 cls.generators.append(Gen(index, name, deg, Vector(deg3d))) m = ((index, -1),) return cls(m).simplify() if cls.auto_simplify else cls(m)
def basis_mons(cls, pred=None, basis: Dict[Vector, List[tuple]] = None): """Return a list of basis (mon, deg).""" pred = pred or cls.pred # noinspection PyArgumentList result = basis or defaultdict(list, {Vector((0, 0, 0)): [()]}) old_ds = set(result) leadings = sorted(cls.rels, key=lambda _m: _m[-1][0]) leadings = {index: list(g) for index, g in groupby(leadings, key=lambda _m: _m[-1][0])} for gen in cls.generators: index, deg3d = gen.index, gen.deg3d ds = list(result) for d in ds: if (d_ := d + deg3d) not in old_ds and pred(d_): for m in result[d]: i_m = m[-1][0] if m else -1 if index == i_m and d in old_ds: e = 1 while pred(d1 := d + deg3d * e): m1 = m[:-1] + ((m[-1][0], m[-1][1] - e),) if index in leadings and any(map(le_dtuple, leadings[index], repeat(m1))): break result[d1].append(m1) e += 1 elif index > i_m: e = 1 while pred(d1 := d + deg3d * e):
def basis_mons_h0(cls, pred=None): """Return a list of $h_0$-structure lines.""" pred = pred or cls.pred def d3d_h0(d3d): return d3d - ((d3d0 := d3d[0]), d3d0, d3d0) # noinspection PyArgumentList result = defaultdict(list, {Vector((0, 0, 0)): [((0, -_S_MAX),)]}) old_ds = set(result) leadings = sorted(cls.rels, key=lambda _m: _m[-1][0]) leadings = {index: list(g) for index, g in groupby(leadings, key=lambda _m: _m[-1][0])} for gen in cls.generators: if gen.index == 0: continue index, deg3d = gen.index, d3d_h0(gen.deg3d) ds = list(result) for d in ds: if (d_ := d + deg3d) not in old_ds and pred(d_): for m in result[d]: i_m = m[-1][0] if m else -1 if index == i_m and d in old_ds: e = 1 while pred(d1 := d + deg3d * e): m1 = m[:-1] + ((m[-1][0], m[-1][1] - e),) if index in leadings and any(map(le_dtuple, leadings[index], repeat(m1))): break result[d1].append(m1) e += 1 elif index > i_m: e = 1 while pred(d1 := d + deg3d * e):
def __init__(self): """`origin_sp` is the screen position of world origin. `unit_screen` is the screen length of world unit length.""" self.origin_sp = Vector( (config["margin_left"], config["win_height"] - config["margin_bottom"])) self.unit_screen = config["camera.unit_screen"]
def add_gens(cls, name_deg_deg3d_s): """Add generators. name_deg_deg3d_s is a list of tuples (name, deg, deg3d).""" index = cls.generators[-1][0] + 1 if cls.generators else 0 for n, d, d3d in name_deg_deg3d_s: if cls.pred(d3d): cls.generators.append(Gen(index, n, d, Vector(d3d))) index += 1
def render_grid_numbers(self): """Draw the grid numbers.""" n_label_step = math.ceil(config["axis_text_sep_screen"] / self.camera.unit_screen) for i in range(0, config["y_max"] + 1, n_label_step): left = self.camera.wp2sp((0, i)) text_pos = left - Vector([config["axis_text_sep_screen"], 0]) if text_pos[0] < 16: text_pos[0] = 16 self.paint.draw_text(str(i), text_pos, config["axis_numbers_color"]) for i in range(0, config["x_max"] + 1, n_label_step): bottom = self.camera.wp2sp((i, 0)) text_pos = bottom + Vector([0, config["axis_text_sep_screen"]]) if text_pos[1] > config["win_height"] - 16: text_pos[1] = config["win_height"] - 16 self.paint.draw_text(str(i), text_pos, config["axis_numbers_color"])
def to_dga(cls, deg_diff: tuple) -> "Type[GbDga]": class_name = f"GbDGA_{GbDga._name_index}" GbDga._name_index += 1 # noinspection PyTypeChecker dct = {'generators': [DgaGen(*gen, None) for gen in cls.generators], 'rels': copy.deepcopy(cls.rels), '_rels_gen_leads': cls._rels_gen_leads.copy(), '_rels_cache': copy.deepcopy(cls._rels_cache), 'key': cls.key, 'pred': cls.pred, 'auto_simplify': cls.auto_simplify, 'deg_diff': Vector(deg_diff)} # noinspection PyTypeChecker return type(class_name, (GbDga,), dct)
def draw_arrow(self, start_pos, end_pos): """ Draw a classic arrow """ d = Vector2c(end_pos) - Vector2c(start_pos) if abs(d) == 0: return d = d / abs(d) d1, d2 = d * (10+5j), d * (10-5j) end_pos = Vector(end_pos) self.draw_line(config["pen_color"], start_pos, end_pos) self.draw_line(config["pen_color"], end_pos - c2Vector(d1), end_pos) self.draw_line(config["pen_color"], end_pos - c2Vector(d2), end_pos)
def exp_collide_point(self, screen_pos, deg=None): """Return if `screen_pos` is in an expansion square.""" for d in (deg, ) if deg else self.expansion: center = self.deg2sp(d) n = self.deg2num_bullets[d] num_edge = math.ceil(math.sqrt(n)) length_edge = self.get_bullet_sep() * (num_edge + 1) size = Vector([length_edge, length_edge]) rect = Rect(myroundv(center - size / 2), myroundv(size)) if rect.collidepoint(*screen_pos): return d return None
def add_gen(cls, name: str, deg, deg3d=(0, 0, 0), diff=None): """Add a new generator and return it.""" index = cls.generators[-1][0] + 1 if cls.generators else 0 if diff is None: diff = set() elif type(diff) is not set: diff = diff.data if diff and cls.deg3d_data(diff) - deg3d != cls.deg_diff: raise BA.MyDegreeError("inconsistent differential degree") cls.generators.append(DgaGen(index, name, deg, Vector(deg3d), diff)) m = ((index, -1),) return cls(m).simplify() if cls.auto_simplify else cls(m)
def addr2sp(self, addr): deg, index = addr if index is None: return self.deg2sp(deg) n = self.deg2num_bullets[deg] if n <= 9: offset = config["bullet.patterns"][ n - 1][index] * self.get_bullet_sep() // 2 return self.deg2sp(deg) + offset else: if deg in self.expansion or deg in self.spread: center = self.deg2sp(deg) n = self.deg2num_bullets[deg] num_edge = math.ceil(math.sqrt(n)) length_edge = self.get_bullet_sep() * (num_edge + 1) size = Vector([length_edge, length_edge]) y, x = divmod(index, num_edge) pos_bullet = (center - size / 2) + Vector( [x + 1, y + 1]) * self.get_bullet_sep() return pos_bullet else: return self.deg2sp(deg)
def new_alg(*, key=None, pred=None, deg_diff=None) -> "Type[GbDga]": """Return a dynamically created subclass of GbDga.""" cls = GbDga class_name = f"GbAlgMod2_{cls._name_index}" cls._name_index += 1 if deg_diff is not None: deg_diff = Vector(deg_diff) else: raise BA.MyDegreeError("degree of differential not supplied") dct = {'generators': [], 'rels': {}, '_rels_gen_leads': set(), '_rels_cache': [], 'key': key, 'pred': pred or GbAlgMod2.pred_default, 'auto_simplify': True, 'deg_diff': deg_diff} # noinspection PyTypeChecker return type(class_name, (cls,), dct)
def sp2addr(self, screen_pos): """* Return None if not on any bullet * Return (deg, None) if on folded bullet * Return (deg, index) if on some bullet""" deg = self.sp2deg(screen_pos) deg_expand = self.exp_collide_point(screen_pos) or ( deg if deg in self.spread else None) if deg_expand: center = self.deg2sp(deg_expand) n = self.deg2num_bullets[deg_expand] num_edge = math.ceil(math.sqrt(n)) length_edge = self.get_bullet_sep() * (num_edge + 1) size = Vector([length_edge, length_edge]) offset = Vector(screen_pos) - (center - size / 2) x, y = myroundv(offset / self.get_bullet_sep()) if 0 < x <= num_edge and 0 < y <= num_edge: index = x - 1 + num_edge * (y - 1) if index < n: return deg_expand, index return None if deg in self.deg2num_bullets: n = self.deg2num_bullets[deg] if 0 < n <= 9: offset = Vector(screen_pos) - Vector(self.deg2sp(deg)) index = self.offset_to_bullet(offset, n) if index is not None: return deg, index else: return None elif n > 9: if math.dist(screen_pos, self.deg2sp(deg)) < 2 * self.get_bullet_radius(): return deg, None else: return None else: return None
def construct_alg(pred, basis, cls_basis, fn: Callable[[Any], set], naming: Callable): """Construct an algebra from basis.""" R = GbAlgMod2.new_alg(pred=pred) # noinspection PyArgumentList R_basis_mons = defaultdict(list, {Vector((0, 0, 0)): [()]}) map_alg = linalg.GradedLinearMapKMod2() image_gens = {} basis_V = basis if basis: d = get_one_element(basis) if type(basis[d]) is not linalg.VectorSpaceMod2: basis_V = {d: linalg.VectorSpaceMod2({m} for m in basis[d]) for d in basis} ds = sorted(d for d in basis if pred(d)) for d in ds: if d == (0, 0, 0): continue BA.Monitor.print(f"{d=}") for x in (basis_V[d] / map_alg.image(d)).simplify().basis(cls_basis): BA.Monitor.print(f"{x=}", 1) gen_name = naming(x, d) R.add_gen(gen_name, d[1], d) index = R.generators[-1][0] image_gens[gen_name] = x ds_R_basis_mons = {d: len(R_basis_mons[d]) for d in R_basis_mons} leadings = [] for d1 in ds_R_basis_mons: if pred(d1 + d): for i in range(ds_R_basis_mons[d1]): m1 = R_basis_mons[d1][i] e = 1 while pred(d2 := d1 + d * e): m2 = m1 + ((index, -e),) # type: tuple if any(map(le_dtuple, leadings, repeat(m2))): break R_basis_mons[d2].append(m2) r2 = R(m2) fr2 = fn(r2.evaluation(image_gens)) map_alg.add_maps_set([(r2.data, fr2)], d2) if map_alg.kernel(d2): R.add_rels_data(map_alg.kernel(d2).basis(set)) map_alg.kernel(d2).clear() leadings = [m for m in R.rels if m[-1][0] == index] break e += 1
def myroundv(t) -> Vector: return Vector(map(round, t))
"""pygame wrapper""" import os import json import pygame import pygame.gfxdraw from algebras.mymath import Vector with open(f"{os.path.dirname(__file__)}\\pen_ss.json", "r") as file: config = json.load(file) config["bullet.patterns.offset"] = [{tuple(pos): i for i, pos in enumerate(pattern)} for pattern in config["bullet.patterns"]] config["bullet.patterns"] = [[Vector(pos) for pos in pattern] for pattern in config["bullet.patterns"]] def myroundv(t) -> Vector: return Vector(map(round, t)) def c2Vector(z: complex): return Vector((z.real, z.imag)) def Vector2c(a): return complex(a[0], a[1]) class Paint: def __init__(self, surface): self.surface = surface self.font = pygame.font.SysFont("Arial", 20)
def sp2wp(self, pos): """Convert screen position to world position.""" return self.flip((Vector(pos) - self.origin_sp) / self.unit_screen)
def render(self): """Draw on the screen.""" if not self.wait_for_update: return else: self.wait_for_update = False bullet_radius = self.get_bullet_radius() # draw background self.render_grid_lines() # draw expansions for deg in self.expansion: if deg not in self.spread: n = self.deg2num_bullets[deg] num_edge = math.ceil(math.sqrt(n)) length_edge = self.get_bullet_sep() * (num_edge + 1) center = self.deg2sp(deg) size = Vector([length_edge, length_edge]) rect = Rect(myroundv(center - size / 2), myroundv(size)) self.paint.draw_rect(config["pen_color"], rect, 1) # draw lines lines = sorted( ((addr1 := self.id2addr[line.src_id], addr2 := self.id2addr[line.tgt_id], math.dist( addr1[0], addr2[0]), line.color) for line in self.ss.lines), key=lambda _l: _l[2]) for addr1, addr2, dist, color in reversed(lines): if dist < 3: line_color = color else: line_color = interpolation(3 / dist, color, config['bg_color']) self.paint.draw_line(line_color, self.addr2sp(addr1), self.addr2sp(addr2)) # draw arrows for arrow in self.ss.arrows: addr1, addr2 = self.id2addr[arrow.src_id], self.id2addr[ arrow.tgt_id] self.paint.draw_arrow(self.addr2sp(addr1), self.addr2sp(addr2)) if self.status == "on_bullet": if self.addr_mouse_down is not None and self.addr_mouse_down[ 1] is not None: self.paint.draw_arrow(self.addr2sp(self.addr_mouse_down), self.pos_current) # draw bullets for deg in self.camera.degs_in_screen(): if deg in self.deg2num_bullets: n = self.deg2num_bullets[deg] if n <= 9: for i in range(n): offset = config["bullet.patterns"][ n - 1][i] * self.get_bullet_sep() / 2 self.paint.draw_circle( self.addr2color((deg, i)), Vector(self.deg2sp(deg)) + offset, bullet_radius) else: num_edge = math.ceil(math.sqrt(n)) length_edge = self.get_bullet_sep() * (num_edge + 1) center = self.deg2sp(deg) size = Vector([length_edge, length_edge]) rect = Rect(myroundv(center - size / 2), myroundv(size)) if deg in self.spread: for i in range(n): y, x = divmod(i, num_edge) pos_bullet = Vector(rect.topleft) + Vector( (x + 1, y + 1)) * self.get_bullet_sep() self.paint.draw_circle(self.addr2color((deg, i)), pos_bullet, bullet_radius) elif deg not in self.expansion: self.paint.draw_circle(config["pen_color"], self.deg2sp(deg), bullet_radius) self.paint.draw_circle(config["pen_color"], self.deg2sp(deg), bullet_radius * 2, False) # draw bullets in expansions for deg in self.expansion: if deg not in self.spread: n = self.deg2num_bullets[deg] num_edge = math.ceil(math.sqrt(n)) length_edge = self.get_bullet_sep() * (num_edge + 1) center = self.deg2sp(deg) size = Vector([length_edge, length_edge]) top_left = myroundv(center - size / 2) for i in range(n): y, x = divmod(i, num_edge) pos_bullet = Vector(top_left) + Vector( (x + 1, y + 1)) * self.get_bullet_sep() self.paint.draw_circle(self.addr2color((deg, i)), pos_bullet, bullet_radius) # enlarge hovered-on bullet if self.id_hover_on is not None: addr = self.id2addr[self.id_hover_on] sp = self.addr2sp(addr) color = self.ss.get_bullet_by_id(self.id_hover_on).color self.paint.draw_circle(color, sp, bullet_radius * 1.5) # draw texts for text_p in self.ss.texts: if self.camera.is_in_screen(text_p.deg): s = text_p.text pos = self.camera.wp2sp(Vector(text_p.deg) + text_p.offset) self.paint.draw_text(s, pos, text_p.color) # draw label if self.id_hover_on is not None: addr = self.id2addr[self.id_hover_on] sp = self.addr2sp(addr) s = self.ss.get_bullet_by_id(self.id_hover_on).label self.paint.draw_text(s, sp + Vector((0, -20))) # draw grid numbers self.render_grid_numbers()
def deg3d_mon(cls, mon: tuple): return sum((cls.get_gen(i).deg3d * -e for i, e in mon), Vector((0, 0, 0)))
for (S4, T4), (S5, T5) in product( Hp(ap, a1), Hp(b2, bp)): for S3, T3 in Hp(a1, a2): yield sum( (h(S6, T6) * h_wrap(S4 | S5 | S7, T4 | T5 | T7) for (S6, T6), (S7, T7) in relation6BA( S1, T1, S2, T2, S3, T3)), A.zero()) for S3, T3 in Hp(b1, b2): yield sum( (h(S6, T6) * h_wrap(S4 | S5 | S7, T4 | T5 | T7) for (S6, T6), (S7, T7) in relation6BB( S1, T1, S2, T2, S3, T3)), A.zero()) def pred_E2(d3d): return d3d[1] <= 500 save_dir = "C:\\Users\\lwnpk\\Documents\\MyProgramData\\Math_AlgTop\\" with open(save_dir + "E2_9_generators.json", "r") as fp: generators = json.load(fp) A = GbAlgMod2.new_alg(pred=pred_E2) for name, *d3d in generators: A.add_gen(name, d3d[1], Vector(d3d))
def deg_mon(mon: tuple): return Vector( (len(mon), sum(DualSteenrodDense.deg_mon(m) for m in mon)))
def c2Vector(z: complex): return Vector((z.real, z.imag))
def flip(world_pos): return Vector((world_pos[0], -world_pos[1]))
def deg_mon(mon: tuple): return Vector((len(mon), sum(SteenrodMilnor.deg_mon(m) for m in mon)))
class RelCache(NamedTuple): deg: int is_rel_gen: bool rel: set deg3d: Vector = Vector((0, 0, 0))