diff --git a/svggen/api/composables/GraphComposable.py b/svggen/api/composables/GraphComposable.py index 8f38db70c5ef88087b8c6980a9f98eea7c50a0c1..d8502d97522fad21a7e9ce33f2a10ed97131b4c9 100644 --- a/svggen/api/composables/GraphComposable.py +++ b/svggen/api/composables/GraphComposable.py @@ -88,6 +88,8 @@ class Graph(Composable, BaseGraph): from svggen.utils.tabs import BeamTabs, BeamTabDecoration, BeamSlotDecoration self.tabify(kw("tabFace", BeamTabs), kw("tabDecoration", BeamTabDecoration), kw("slotFace", None), kw("slotDecoration", BeamSlotDecoration)) + if kw("joint", None): + self.jointify(**kwargs) self.place() ''' diff --git a/svggen/api/composables/graph/Drawing.py b/svggen/api/composables/graph/Drawing.py index 0edf90154d6b210c3e4a0219c0fabd158c8d3c44..b132363df129a9e6a4d514ad8133c55cd70815c3 100644 --- a/svggen/api/composables/graph/Drawing.py +++ b/svggen/api/composables/graph/Drawing.py @@ -49,6 +49,8 @@ class Drawing: angle = angles[1][0] - angles[0][0] if angle == 0: edge = Flat() + elif e.edgeType is "BEND": + edge = Flex(angle=angle) else: edge = Fold(angle=angle) else: diff --git a/svggen/api/composables/graph/Face.py b/svggen/api/composables/graph/Face.py index d693661d6a0ca94e98ecbf953d2319b46616b58d..286183b8bc8334d9d42c5f20bb5f1fc085cc902e 100644 --- a/svggen/api/composables/graph/Face.py +++ b/svggen/api/composables/graph/Face.py @@ -246,7 +246,8 @@ class Face(object): coords2D = self.get2DCoords() coords3D = self.get3DCoords() - for (i, e) in enumerate(self.edges): + # Follow all non-joints before joints + for (i, e) in sorted(enumerate(self.edges), key=lambda x : x[1].isJoint()): # XXX hack: don't follow small edges if e is None or e.isTab(): continue @@ -280,10 +281,13 @@ class Face(object): x = RotateXTo(ptb, pta) - r2d = np.eye(4) - r2d = np.dot(x, r2d) - r2d = np.dot(MoveOriginTo(pta), r2d) - t2d = np.dot(transform2D, r2d) + if e.isJoint(): + t2d = None + else: + r2d = np.eye(4) + r2d = np.dot(x, r2d) + r2d = np.dot(MoveOriginTo(pta), r2d) + t2d = np.dot(transform2D, r2d) r3d = RotateX(np.deg2rad(a[0]+da[0])) r3d = np.dot(x, r3d) @@ -293,6 +297,17 @@ class Face(object): f.place(e, t2d, t3d, facelists, flind) # end for faces.iteritems + for e in self.edges: + if e.isJoint(): + index = self.edgeIndex(e.name) + # print "Jointing ", e.name, "on face", self.name, index + newPts, newEdges = e.joint.go(self, e) + self.pts2d[index:index] = newPts + self.edges[index:index+1] = newEdges + for newEdge in newEdges: + newEdge.join(newEdge.length, self) + e.remove(self) + self.placeagain() def getTriangleDict(self): @@ -330,8 +345,7 @@ class Face(object): name = self.name + ".d%d.e%d" % (i,j) pt1 = np.dot(self.transform2D, np.array(list(e[0][j-1]) + [0,1]))[0:2] pt2 = np.dot(self.transform2D, np.array(list(e[0][j]) + [0,1]))[0:2] - # XXX use EdgeType appropriately - # ??? deleted EdgeType + # XXX use EdgeType appropriately edges.append([name, pt1, pt2, 1]) else: name = self.name + ".d%d" % i diff --git a/svggen/api/composables/graph/Graph.py b/svggen/api/composables/graph/Graph.py index 894028668db8744286a379240eca9728b4830bea..ffe397eac2300b11221a7594f64d22ebd0a43612 100644 --- a/svggen/api/composables/graph/Graph.py +++ b/svggen/api/composables/graph/Graph.py @@ -83,13 +83,13 @@ class Graph(): self.rebuildEdges() return self - def attachFace(self, fromEdge, newFace, newEdge, prefix=None, angle=0): + def attachFace(self, fromEdge, newFace, newEdge, prefix=None, angle=0, edgeType=None, joints=None): # XXX should set angle from a face, not absolute angle of the face self.addFace(newFace, prefix) if fromEdge is not None: newEdge = prefixString(prefix, newEdge) - self.mergeEdge(fromEdge, newEdge, angle=angle) + self.mergeEdge(fromEdge, newEdge, angle=angle, edgeType=edgeType, joints=joints) def delFace(self, facename): for (i, f) in enumerate(self.faces): @@ -140,7 +140,7 @@ class Graph(): def addTab(self, edge1, edge2, angle=0, width=10): self.mergeEdge(edge1, edge2, angle=angle, tabWidth=width) - def mergeEdge(self, edge1, edge2, angle=0, tabWidth=None, swap=False): + def mergeEdge(self, edge1, edge2, angle=0, tabWidth=None, edgeType=None, joints=None, swap=False): e1 = self.getEdge(edge1) e2 = self.getEdge(edge2) if e1 is None: @@ -161,6 +161,11 @@ class Graph(): e2.mergeWith(e1, angle=angle, flip=True, tabWidth=tabWidth) self.edges.remove(e1) + e2.setType(edgeType) + if joints: + for joint in joints.joints: + e2.addJoint(joint) + return self def splitEdge(self, edge): @@ -181,6 +186,13 @@ class Graph(): self.rebuildEdges() return new_edges_and_faces + def jointify(self, **kwargs): + for e in self.edges: + if e.isNotFlat() and "joint" in kwargs: + e.setType("JOINT") + e.addJoint(kwargs["joint"]) + #print "jointing ", e.name + def tabify(self, tabFace=None, tabDecoration=None, slotFace=None, slotDecoration=None, **kwargs): for e in self.edges: if e.isTab(): diff --git a/svggen/api/composables/graph/HyperEdge.py b/svggen/api/composables/graph/HyperEdge.py index 1d5e81fe7ad1e859f14c220f793fa29a5cd9ab73..a6c09e85bc80917120ebab8ac569db89a92b4923 100644 --- a/svggen/api/composables/graph/HyperEdge.py +++ b/svggen/api/composables/graph/HyperEdge.py @@ -3,6 +3,9 @@ from svggen.utils import mymath as np class HyperEdge: + #ANDYTODO: transform these into sublclasses of HyperEdge and/or componenet + edgeTypes = ["FOLD", "BEND", "JOINT"] + @staticmethod def edge(allEdges, name, length, face, angle=0, flip=False): if allEdges is not None: @@ -25,6 +28,8 @@ class HyperEdge: self.tabWidth = None self.pts2D = None self.pts3D = None + self.edgeType = "FOLD" + self.joint = None #self.pt1 = pt1 #self.pt2 = pt2 @@ -45,11 +50,15 @@ class HyperEdge: self.name = name def isNotFlat(self): - return any((x[0] for x in self.faces.values())) + return self.edgeType is "JOINT" or \ + (self.edgeType is "FOLD" and any((x[0] for x in self.faces.values()))) def isTab(self): return self.tabWidth is not None + def isJoint(self): + return self.edgeType is "JOINT" and self.joint is not None + def setAngle(self, face, angle, flip=False): if face in self.faces: self.faces[face] = (angle, flip) @@ -136,6 +145,20 @@ class HyperEdge: self.pts2D = pts2D self.pts3D = pts3D + def setType(self, edgeType): + if edgeType is None: + return # do nothing + if edgeType not in self.edgeTypes: + raise Exception("Invalid edge type!") + self.edgeType = edgeType + + def addJoint(self, joint): + if not self.edgeType is "JOINT": + raise Exception("Trying to add joints to a non-joint edge") + # if not isinstance(joint, Joint.Joint): + # raise Exception("Not a joint!") + self.joint = joint + def __eq__(self, other): return self.name == other.name diff --git a/svggen/api/composables/graph/Joint.py b/svggen/api/composables/graph/Joint.py new file mode 100644 index 0000000000000000000000000000000000000000..3c410a1659f4ddda8c7886520ad8fedd6b786c70 --- /dev/null +++ b/svggen/api/composables/graph/Joint.py @@ -0,0 +1,96 @@ +from svggen.api.composables.graph.HyperEdge import HyperEdge +import svggen.utils.mymath as np + +class Joint: + def __init__(self, **kwargs): + self.kwargs = kwargs + def go(face, edge): + pass + +class FingerJoint(Joint): + def go(self, face, edge): + inset = False + edgename = face.name + edge.name + index = face.edgeIndex(edge.name) + angle, flip = edge.faces[face] + + thickness = self.kwargs["thickness"] + + coords = face.edgeCoords(index) + length = face.edgeLength(index) + if inset: + length -= thickness + + pt1 = np.array(coords[0]) + pt2 = np.array(coords[1]) + + n = int(max(3, round(length * 1.0 / thickness))) # number of fingers + dt = length * 1.0 / n # actual thickness of fingers + dlp = thickness / 2. + dln = thickness / 2. # Only works for 90deg angles; np.sqrt(2) max + dl = dlp + dln # actual length of fingers + + flip = flip and (n % 2 == 1) + + dpt = (pt2 - pt1) * 1.0 * dt / face.edgeLength(index) + ppt = np.array((dpt[1], -dpt[0])) / dt + + newEdges = [] + newPts = [] + newPt = coords[0] + + def addNew(newEdge, newPt): + newEdges.append(newEdge) + newPts.append(newPt) + newEdge.join(newEdge.length, face) + + if inset: + # inset from the edge for 3 face corners + newPt = newPt - ppt * dln + newEdge = HyperEdge(edgename + "fjx1", dln) + addNew(newEdge, newPt) + + newPt = newPt + dpt * thickness / dt / 2.0 + newEdge = HyperEdge(edgename + "fjx2", dt) + addNew(newEdge, newPt) + + newPt = newPt + ppt * dln + newEdge = HyperEdge(edgename + "fjx3", dln) + addNew(newEdge, newPt) + + if flip: + newPt = newPt + ppt * dlp + newEdge = HyperEdge(edgename + "fj0", dlp) + else: + newPt = newPt - ppt * dln + newEdge = HyperEdge(edgename + "fj0", dln) + + + for i in range(int(n)): + addNew(newEdge, newPt) + + newPt = newPt + dpt + newEdge = HyperEdge(edgename + "fjd%d" % i, dt) + addNew(newEdge, newPt) + + if flip: + newPt = newPt - ppt * dl + else: + newPt = newPt + ppt * dl + newEdge = HyperEdge(edgename + "fjp%d" % i, dl) + flip = not flip + + if not flip: + addNew(newEdge, newPt) + else: + newPt = newPt - ppt * dl + + if inset: + newPt = newPt + dpt * thickness / dt / 2.0 + newEdge = HyperEdge(edgename + "fjy", dt) + addNew(newEdge, newPt) + + newEdge = HyperEdge(edgename + "fjn", dln) + newEdges.append(newEdge) + + return newPts, newEdges